diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..b7d5dc9 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +* @takezoe @xuwei-k diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..5ace460 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,6 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..d5bae3f --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,45 @@ +name: build + +on: [push, pull_request] + +jobs: + build: + runs-on: ubuntu-latest + timeout-minutes: 20 + strategy: + matrix: + java: [11] + steps: + - uses: actions/checkout@v5 + - name: Cache + uses: actions/cache@v4 + env: + cache-name: cache-sbt-libs + with: + path: | + ~/.ivy2/cache + ~/.sbt + ~/.coursier + key: build-${{ env.cache-name }}-${{ hashFiles('build.sbt') }} + - name: Set up JDK + uses: actions/setup-java@v5 + with: + distribution: temurin + java-version: ${{ matrix.java }} + - uses: sbt/setup-sbt@v1 + - name: Run tests + run: | + git clone https://github.com/gitbucket/gitbucket.git + cd gitbucket + sbt publishLocal + cd ../ + sbt test + - name: Assembly + run: sbt assembly + - name: Upload artifacts + uses: actions/upload-artifact@v5 + with: + name: gitbucket-gist-plugin-java${{ matrix.java }}-${{ github.sha }} + path: ./target/scala-2.13/*.jar + + diff --git a/.gitignore b/.gitignore index 938d6b5..ae82060 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ lib_managed/ src_managed/ project/boot/ project/plugins/project/ +.bsp/ # Scala-IDE specific .scala_dependencies @@ -22,4 +23,11 @@ project/plugins/project/ # Ensime .ensime -.ensime_cache/ \ No newline at end of file +.ensime_cache/ + +# Metals +.bloop/ +.metals/ +.vscode/ +**/metals.sbt + diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 69d3656..0000000 --- a/.travis.yml +++ /dev/null @@ -1,6 +0,0 @@ -sudo: false -language: scala -jdk: -- oraclejdk8 -script: -- sbt test diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/README.md b/README.md index 42dc4bc..2f9d5fa 100644 --- a/README.md +++ b/README.md @@ -1,23 +1,40 @@ -# gitbucket-gist-plugin - -This is an example of GitBucket plug-in. This plug-in provides code snippet repository like Gist. - -Plugin version | GitBucket version -:--------------|:-------------------- -4.4.x | 4.8.x -4.2.x, 4.3.x | 4.2.x, 4.3.x, 4.4.x, 4.5.x, 4.6.x, 4.7.x -4.0.x | 4.0.x, 4.1.x -3.13.x | 3.13.x -3.12.x | 3.12.x -3.11.x | 3.11.x -3.10.x | 3.10.x -3.7.x | 3.7.x, 3.8.x, 3.9.x -3.6.x | 3.6.x - +# gitbucket-gist-plugin [![build](https://github.com/gitbucket/gitbucket-gist-plugin/workflows/build/badge.svg?branch=master)](https://github.com/gitbucket/gitbucket-gist-plugin/actions?query=workflow%3Abuild+branch%3Amaster) + +This is a GitBucket plug-in which provides code snippet repository like Gist. + +| Plugin version | GitBucket version | +|:---------------|:------------------| +| 4.23.x | 4.40.x - | +| 4.22.x | 4.37.x - | +| 4.21.x | 4.36.x - | +| 4.20.x | 4.35.x - | +| 4.19.x | 4.34.x - | +| 4.18.x | 4.32.x - 4.33.x | +| 4.17.x | 4.30.x - 4.31.x | +| 4.16.x | 4.26.x - 4.29.x | +| 4.15.x | 4.25.x | +| 4.13.x, 4.14.x | 4.23.x - 4.24.x | +| 4.12.x | 4.21.x - 4.22.x | +| 4.11.x | 4.19.x - 4.20.x | +| 4.10.x | 4.15.x - 4.18.x | +| 4.9.x | 4.14.x | +| 4.8.x | 4.11.x - 4.13.x | +| 4.7.x | 4.11.x | +| 4.6.x | 4.10.x | +| 4.5.x | 4.9.x | +| 4.4.x | 4.8.x | +| 4.2.x, 4.3.x | 4.2.x - 4.7.x | +| 4.0.x | 4.0.x - 4.1.x | +| 3.13.x | 3.13.x | +| 3.12.x | 3.12.x | +| 3.11.x | 3.11.x | +| 3.10.x | 3.10.x | +| 3.7.x | 3.7.x - 3.9.x | +| 3.6.x | 3.6.x | ## Installation -Download jar file from [the release page](https://github.com/gitbucket/gitbucket-gist-plugin/releases) and put into `GITBUCKET_HOME/plugins`. +Download jar file from [Releases page](https://github.com/gitbucket/gitbucket-gist-plugin/releases) and put into `GITBUCKET_HOME/plugins`. **Note:** If you had used this plugin with GitBucket 3.x, it does not work after upgrading to GitBucket 4.x. Solution is below: @@ -30,6 +47,4 @@ See [Connect to H2 database](https://github.com/gitbucket/gitbucket/wiki/Connect ## Build from source -1. Hit `./sbt.sh package` in the root directory of this repository. -2. Copy `target/scala-2.11/gitbucket-gist-plugin_2.11-x.x.x.jar` into `GITBUCKET_HOME/plugins`. -3. Restart GitBucket. +Run `sbt assembly` and copy generated `/target/scala-2.13/gitbucket-gist-plugin-x.x.x.jar` to `~/.gitbucket/plugins/` (If the directory does not exist, create it by hand before copying the jar), or just run `sbt install`. diff --git a/build.sbt b/build.sbt index c06d976..49c6ffd 100644 --- a/build.sbt +++ b/build.sbt @@ -1,19 +1,10 @@ -val Organization = "io.github.gitbucket" -val ProjectName = "gitbucket-gist-plugin" -val ProjectVersion = "4.4.0" +organization := "io.github.gitbucket" +name := "gitbucket-gist-plugin" +version := "4.23.0" +scalaVersion := "2.13.18" +gitbucketVersion := "4.44.0" -lazy val root = (project in file(".")).enablePlugins(SbtTwirl) +scalacOptions := Seq("-deprecation", "-feature", "-language:postfixOps") +Compile / javacOptions ++= Seq("-target", "11", "-source", "11") -organization := Organization -name := ProjectName -version := ProjectVersion -scalaVersion := "2.11.8" - -libraryDependencies ++= Seq( - "io.github.gitbucket" %% "gitbucket" % "4.8" % "provided", - "com.typesafe.play" %% "twirl-compiler" % "1.0.4" % "provided", - "javax.servlet" % "javax.servlet-api" % "3.1.0" % "provided" -) - -scalacOptions := Seq("-deprecation", "-feature", "-language:postfixOps", "-Ybackend:GenBCode", "-Ydelambdafy:method", "-target:jvm-1.8") -javacOptions in compile ++= Seq("-target", "8", "-source", "8") +useJCenter := true diff --git a/install-local.sh b/install-local.sh deleted file mode 100755 index 0c89af9..0000000 --- a/install-local.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh -./sbt.sh package -cp target/scala-2.11/gitbucket-gist-plugin_2.11-4.4.0.jar ~/.gitbucket/plugins/ diff --git a/project/build.properties b/project/build.properties index da4bbed..a360cca 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version = 0.13.9 \ No newline at end of file +sbt.version = 1.11.7 diff --git a/project/plugins.sbt b/project/plugins.sbt index 0c88bf6..3fcddcc 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,3 +1 @@ -logLevel := Level.Warn - -addSbtPlugin("com.typesafe.sbt" % "sbt-twirl" % "1.0.4") +addSbtPlugin("io.github.gitbucket" % "sbt-gitbucket-plugin" % "1.6.0") \ No newline at end of file diff --git a/sbt-launch-0.13.9.jar b/sbt-launch-0.13.9.jar deleted file mode 100644 index c065b47..0000000 Binary files a/sbt-launch-0.13.9.jar and /dev/null differ diff --git a/sbt.bat b/sbt.bat deleted file mode 100644 index 49aa9ea..0000000 --- a/sbt.bat +++ /dev/null @@ -1,2 +0,0 @@ -set SCRIPT_DIR=%~dp0 -java -Dsbt.log.noformat=true -XX:+CMSClassUnloadingEnabled -XX:MaxPermSize=256m -Xmx512M -Xss2M -jar "%SCRIPT_DIR%\sbt-launch-0.13.9.jar" %* diff --git a/sbt.sh b/sbt.sh deleted file mode 100755 index 793a3c5..0000000 --- a/sbt.sh +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/sh -java -Dsbt.log.noformat=true -XX:+CMSClassUnloadingEnabled -XX:MaxPermSize=256m -Xmx512M -Xss2M -jar `dirname $0`/sbt-launch-0.13.9.jar "$@" diff --git a/src/main/resources/gitbucket/gist/assets/style.css b/src/main/resources/gitbucket/gist/assets/style.css index cb2c45e..2226b23 100644 --- a/src/main/resources/gitbucket/gist/assets/style.css +++ b/src/main/resources/gitbucket/gist/assets/style.css @@ -1,5 +1,7 @@ div.head { - padding: 15px; + padding-top: 15px; + padding-left: 15px; + padding-right: 15px; } div.list-markup { @@ -16,4 +18,11 @@ pre.list-code { background-color: transparent; border: 1px solid rgba(0, 0, 0, 0.15); padding-left: 25px; -} \ No newline at end of file +} + +@media (min-width: 767px) { + div.gist-content { + width: 980px; + margin: auto; + } +} diff --git a/src/main/resources/update/gitbucket-gist_2.0.xml b/src/main/resources/update/gitbucket-gist_2.0.xml index 81ceda8..c8b843e 100644 --- a/src/main/resources/update/gitbucket-gist_2.0.xml +++ b/src/main/resources/update/gitbucket-gist_2.0.xml @@ -24,14 +24,13 @@ - + - diff --git a/src/main/scala/Plugin.scala b/src/main/scala/Plugin.scala index cefe0bf..830e64c 100755 --- a/src/main/scala/Plugin.scala +++ b/src/main/scala/Plugin.scala @@ -25,7 +25,27 @@ class Plugin extends gitbucket.core.plugin.Plugin { new Version("4.2.0", new LiquibaseMigration("update/gitbucket-gist_4.2.xml") ), - new Version("4.4.0") + new Version("4.4.0"), + new Version("4.5.0"), + new Version("4.6.0"), + new Version("4.7.0"), + new Version("4.8.0"), + new Version("4.9.0"), + new Version("4.9.1"), + new Version("4.10.0"), + new Version("4.11.0"), + new Version("4.12.0"), + new Version("4.13.0"), + new Version("4.14.0"), + new Version("4.15.0"), + new Version("4.16.0"), + new Version("4.17.0"), + new Version("4.18.0"), + new Version("4.19.0"), + new Version("4.20.0"), + new Version("4.21.0"), + new Version("4.22.0"), + new Version("4.23.0") ) override def initialize(registry: PluginRegistry, context: ServletContext, settings: SystemSettings): Unit = { diff --git a/src/main/scala/gitbucket/gist/controller/GistController.scala b/src/main/scala/gitbucket/gist/controller/GistController.scala index 7a092da..988ef1c 100644 --- a/src/main/scala/gitbucket/gist/controller/GistController.scala +++ b/src/main/scala/gitbucket/gist/controller/GistController.scala @@ -2,14 +2,12 @@ package gitbucket.gist.controller import java.io.File import gitbucket.core.view.helpers -import io.github.gitbucket.scalatra.forms._ +import org.scalatra.forms._ import gitbucket.core.controller.ControllerBase import gitbucket.core.service.AccountService import gitbucket.core.service.RepositoryService.RepositoryInfo import gitbucket.core.util._ -import gitbucket.core.util.Directory._ -import gitbucket.core.util.ControlUtil._ import gitbucket.core.util.Implicits._ import gitbucket.core.view.helpers._ @@ -19,12 +17,14 @@ import gitbucket.gist.util._ import gitbucket.gist.util.GistUtils._ import gitbucket.gist.util.Configurations._ import gitbucket.gist.html +import gitbucket.gist.js -import org.apache.commons.io.FileUtils import org.eclipse.jgit.api.Git import org.eclipse.jgit.lib._ import org.scalatra.Ok import play.twirl.api.Html +import play.twirl.api.JavaScript +import scala.util.Using class GistController extends GistControllerBase with GistService with GistCommentService with AccountService with GistEditorAuthenticator with UsersAuthenticator @@ -50,7 +50,7 @@ trait GistControllerBase extends ControllerBase { case s => s.toInt } val result = getVisibleGists((page - 1) * Limit, Limit, context.loginAccount) - val count = countPublicGists() + val count = countVisibleGists(context.loginAccount) val gists: Seq[(Gist, GistInfo)] = result.map { gist => val userName = gist.userName @@ -58,7 +58,7 @@ trait GistControllerBase extends ControllerBase { val files = getGistFiles(userName, repoName) val (fileName, source) = files.head - (gist, GistInfo(fileName, getLines(source), files.length, getForkedCount(userName, repoName), getCommentCount(userName, repoName))) + (gist, GistInfo(fileName, getLines(fileName, source), files.length, getForkedCount(userName, repoName), getCommentCount(userName, repoName))) } html.list(None, gists, page, page * Limit < count) @@ -68,6 +68,17 @@ trait GistControllerBase extends ControllerBase { _gist(params("userName"), Some(params("repoName"))) } + get("/gist/:userName/:repoName.js"){ + val userName = params("userName") + val repoName = params("repoName") + getGist(userName, repoName) match { + case Some(gist) => + _embedJs(gist, userName, repoName, "master") + case None => + NotFound() + } + } + get("/gist/:userName/:repoName/:revision"){ _gist(params("userName"), Some(params("repoName")), params("revision")) } @@ -77,20 +88,21 @@ trait GistControllerBase extends ControllerBase { val repoName = params("repoName") val gitdir = new File(GistRepoDir, userName + "/" + repoName) if(gitdir.exists){ - using(Git.open(gitdir)){ git => + Using.resource(Git.open(gitdir)){ git => val files: Seq[(String, JGitUtil.ContentInfo)] = JGitUtil.getFileList(git, "master", ".").map { file => - (if(isGistFile(file.name)) "" else file.name) -> JGitUtil.getContentInfo(git, file.name, file.id) + (if(isGistFile(file.name)) "" else file.name) -> JGitUtil.getContentInfo(git, file.name, file.id, true) } - html.edit(getGist(userName, repoName), files) + html.edit(getGist(userName, repoName), files, None) } } }) post("/gist/_new")(usersOnly { - if(context.loginAccount.isDefined){ - val loginAccount = context.loginAccount.get - val files = getFileParameters(true) + val loginAccount = context.loginAccount.get + val userName = params.getOrElse("userName", loginAccount.userName) + if(isEditable(userName, loginUserGroups)) { + val files = getFileParameters() if(files.isEmpty){ redirect(s"/gist") @@ -99,14 +111,14 @@ trait GistControllerBase extends ControllerBase { val description = params("description") // Create new repository - val repoName = StringUtil.md5(loginAccount.userName + " " + datetime(new java.util.Date())) - val gitdir = new File(GistRepoDir, loginAccount.userName + "/" + repoName) + val repoName = StringUtil.md5(userName + " " + datetime(new java.util.Date())) + val gitdir = new File(GistRepoDir, userName + "/" + repoName) gitdir.mkdirs() - JGitUtil.initRepository(gitdir) + JGitUtil.initRepository(gitdir, "master") // Insert record registerGist( - loginAccount.userName, + userName, repoName, getTitle(files.head._1, repoName), description, @@ -114,13 +126,13 @@ trait GistControllerBase extends ControllerBase { ) // Commit files - using(Git.open(gitdir)){ git => + Using.resource(Git.open(gitdir)){ git => commitFiles(git, loginAccount, "Initial commit", files) } - redirect(s"/gist/${loginAccount.userName}/${repoName}") + redirect(s"/gist/${userName}/${repoName}") } - } + } else Unauthorized() }) post("/gist/:userName/:repoName/edit")(editorOnly { @@ -128,7 +140,7 @@ trait GistControllerBase extends ControllerBase { val repoName = params("repoName") val loginAccount = context.loginAccount.get - val files = getFileParameters(true) + val files = getFileParameters() val description = params("description") val mode = Mode.from(params("mode")) @@ -143,7 +155,7 @@ trait GistControllerBase extends ControllerBase { // Commit files val gitdir = new File(GistRepoDir, userName + "/" + repoName) - using(Git.open(gitdir)){ git => + Using.resource(Git.open(gitdir)){ git => val commitId = commitFiles(git, loginAccount, "Update", files) // update refs @@ -155,14 +167,14 @@ trait GistControllerBase extends ControllerBase { refUpdate.update() } - redirect(s"/gist/${loginAccount.userName}/${repoName}") + redirect(s"/gist/${userName}/${repoName}") }) get("/gist/:userName/:repoName/delete")(editorOnly { val userName = params("userName") val repoName = params("repoName") - if(isEditable(userName)){ + if(isEditable(userName, loginUserGroups)){ deleteGist(userName, repoName) val gitdir = new File(GistRepoDir, userName + "/" + repoName) @@ -177,15 +189,11 @@ trait GistControllerBase extends ControllerBase { val repoName = params("repoName") val gitdir = new File(GistRepoDir, userName + "/" + repoName) - using(Git.open(gitdir)){ git => + Using.resource(Git.open(gitdir)){ git => JGitUtil.getCommitLog(git, "master") match { case Right((revisions, hasNext)) => { val commits = revisions.map { revision => - defining(JGitUtil.getRevCommitFromId(git, git.getRepository.resolve(revision.id))){ revCommit => - JGitUtil.getDiffs(git, revision.id) match { case (diffs, oldCommitId) => - (revision, diffs) - } - } + (revision, JGitUtil.getDiffs(git, None, revision.id, true, false)) } val gist = getGist(userName, repoName).get @@ -196,11 +204,11 @@ trait GistControllerBase extends ControllerBase { gist, getForkedCount(originUserName, originRepoName), GistRepositoryURL(gist, baseUrl, context.settings), - isEditable(userName), + isEditable(userName, loginUserGroups), commits ) } - case Left(_) => NotFound + case Left(_) => NotFound() } } } @@ -212,18 +220,17 @@ trait GistControllerBase extends ControllerBase { val fileName = params("fileName") val gitdir = new File(GistRepoDir, userName + "/" + repoName) if(gitdir.exists){ - using(Git.open(gitdir)){ git => + Using.resource(Git.open(gitdir)){ git => val gist = getGist(userName, repoName).get if(gist.mode == "PUBLIC" || context.loginAccount.exists(x => x.isAdmin || x.userName == userName)){ JGitUtil.getFileList(git, revision, ".").find(_.name == fileName).map { file => - defining(JGitUtil.getContentFromId(git, file.id, false).get){ bytes => - RawData(FileUtil.getContentType(file.name, bytes), bytes) - } - } getOrElse NotFound - } else Unauthorized + val bytes = JGitUtil.getContentFromId(git, file.id, false).get + RawData(FileUtil.getMimeType(file.name, bytes), bytes) + } getOrElse NotFound() + } else Unauthorized() } - } else NotFound + } else NotFound() } get("/gist/:userName/:repoName/download/*"){ @@ -235,13 +242,7 @@ trait GistControllerBase extends ControllerBase { val userName = params("userName") val repoName = params("repoName") - val workDir = getDownloadWorkDir(userName, repoName, session.getId) - if(workDir.exists) { - FileUtils.deleteDirectory(workDir) - } - workDir.mkdirs - - using(Git.open(new File(GistRepoDir, userName + "/" + repoName))){ git => + Using.resource(Git.open(new File(GistRepoDir, userName + "/" + repoName))){ git => val revCommit = JGitUtil.getRevCommitFromId(git, git.getRepository.resolve("master")) contentType = "application/octet-stream" @@ -265,13 +266,20 @@ trait GistControllerBase extends ControllerBase { getUserGists(userName, context.loginAccount.map(_.userName), 0, Limit), countUserGists(userName, context.loginAccount.map(_.userName)) ) + + val createSnippet = context.loginAccount.exists { loginAccount => + loginAccount.userName == userName || getGroupsByUserName(loginAccount.userName).contains(userName) + } + getAccountByUserName(userName).map { account => html.profile( - account, - if(account.isGroupAccount) Nil else getGroupsByUserName(userName), - result._1 + account = account, + groupNames = if(account.isGroupAccount) Nil else getGroupsByUserName(userName), + extraMailAddresses = getAccountExtraMailAddresses(userName), + gists = result._1, + createSnippet = createSnippet ) - } getOrElse NotFound + } getOrElse NotFound() } get("/gist/:userName"){ @@ -279,12 +287,16 @@ trait GistControllerBase extends ControllerBase { } get("/gist/_new")(usersOnly { - html.edit(None, Seq(("", JGitUtil.ContentInfo("text", None, Some("UTF-8"))))) + val userName = params.get("userName") + + if(isEditable(userName.getOrElse(context.loginAccount.get.userName), loginUserGroups)){ + html.edit(None, Seq(("", JGitUtil.ContentInfo("text", None, None, Some("UTF-8")))), userName) + } else Unauthorized() }) get("/gist/_add"){ val count = params("count").toInt - html.editor(count, "", JGitUtil.ContentInfo("text", None, Some("UTF-8"))) + html.editor(count, "", JGitUtil.ContentInfo("text", None, None, Some("UTF-8"))) } //////////////////////////////////////////////////////////////////////////////// @@ -317,7 +329,7 @@ trait GistControllerBase extends ControllerBase { redirect(s"/gist/${loginAccount.userName}/${repoName}") - } getOrElse NotFound + } getOrElse NotFound() } }) @@ -331,9 +343,9 @@ trait GistControllerBase extends ControllerBase { getForkedCount(userName, repoName), GistRepositoryURL(gist, baseUrl, context.settings), getForkedGists(userName, repoName), - isEditable(userName) + isEditable(userName, loginUserGroups) ) - } getOrElse NotFound + } getOrElse NotFound() } //////////////////////////////////////////////////////////////////////////////// @@ -347,18 +359,27 @@ trait GistControllerBase extends ControllerBase { val repoName = params("repoName") contentType = "text/html" - helpers.markdown(params("content"), - RepositoryInfo( - owner = userName, - name = repoName, - repository = null, - issueCount = 0, - pullCount = 0, - forkedCount = 0, - branchList = Nil, - tags = Nil, - managers = Nil - ), false, false, false, false) + helpers.markdown( + markdown = params("content"), + repository = RepositoryInfo( + owner = userName, + name = repoName, + repository = null, + issueCount = 0, + pullCount = 0, + forkedCount = 0, + milestoneCount = 0, + branchList = Nil, + tags = Nil, + managers = Nil + ), + branch = "master", + enableWikiLink = false, + enableRefsLink = false, + enableLineBreaks = false, + enableAnchor = false, + enableTaskList = true + ) } post("/gist/:userName/:repoName/_comment", commentForm)(usersOnly { form => @@ -369,7 +390,7 @@ trait GistControllerBase extends ControllerBase { getGist(userName, repoName).map { gist => registerGistComment(userName, repoName, form.content, loginAccount.userName) redirect(s"/gist/${userName}/${repoName}") - } getOrElse NotFound + } getOrElse NotFound() }) ajaxPost("/gist/:userName/:repoName/_comments/:commentId/_delete")(usersOnly { @@ -391,16 +412,23 @@ trait GistControllerBase extends ControllerBase { getGist(userName, repoName).flatMap { gist => getGistComment(userName, repoName, commentId).map { comment => params.get("dataType") collect { - case t if t == "html" => gitbucket.gist.html.commentedit( - comment.content, comment.commentId, comment.userName, comment.repositoryName) + case t if t == "html" => gitbucket.gist.html.commentedit(gist, comment.content, comment.commentId) } getOrElse { contentType = formats("json") org.json4s.jackson.Serialization.write( - Map("content" -> gitbucket.core.view.Markdown.toHtml(comment.content, gist.toRepositoryInfo, false, true, true, true)) + Map("content" -> gitbucket.core.view.Markdown.toHtml( + markdown = comment.content, + repository = gist.toRepositoryInfo, + branch = "master", + enableWikiLink = false, + enableRefsLink = true, + enableAnchor = true, + enableLineBreaks = true + )) ) } } - } getOrElse NotFound + } getOrElse NotFound() }) ajaxPost("/gist/:userName/:repoName/_comments/:commentId/_update", commentForm)(usersOnly { form => @@ -438,7 +466,7 @@ trait GistControllerBase extends ControllerBase { val repoName = gist.repositoryName val files = getGistFiles(userName, repoName, revision) val (fileName, source) = files.head - (gist, GistInfo(fileName, getLines(source), files.length, getForkedCount(userName, repoName), getCommentCount(userName, repoName))) + (gist, GistInfo(fileName, getLines(fileName, source), files.length, getForkedCount(userName, repoName), getCommentCount(userName, repoName))) } val fullName = getAccountByUserName(userName).get.fullName @@ -462,6 +490,18 @@ trait GistControllerBase extends ControllerBase { } } + private def _embedJs(gist: Gist, userName: String, repoName: String, revision: String): JavaScript = { + val originUserName = gist.originUserName.getOrElse(userName) + val originRepoName = gist.originRepositoryName.getOrElse(repoName) + + js.detail( + gist, + GistRepositoryURL(gist, baseUrl, context.settings), + revision, + getGistFiles(userName, repoName, revision) + ) + } + private def _gistDetail(gist: Gist, userName: String, repoName: String, revision: String): Html = { val originUserName = gist.originUserName.getOrElse(userName) val originRepoName = gist.originRepositoryName.getOrElse(repoName) @@ -473,35 +513,33 @@ trait GistControllerBase extends ControllerBase { revision, getGistFiles(userName, repoName, revision), getGistComments(userName, repoName), - isEditable(userName) + isEditable(userName, loginUserGroups) ) } private def getGistFiles(userName: String, repoName: String, revision: String = "master"): Seq[(String, String)] = { val gitdir = new File(GistRepoDir, userName + "/" + repoName) - using(Git.open(gitdir)){ git => + Using.resource(Git.open(gitdir)){ git => JGitUtil.getFileList(git, revision, ".").map { file => file.name -> StringUtil.convertFromByteArray(JGitUtil.getContentFromId(git, file.id, true).get) } } } - private def getFileParameters(flatten: Boolean): Seq[(String, String)] = { + private def getFileParameters(): Seq[(String, String)] = { val count = params("count").toInt - if(flatten){ - (0 to count - 1).flatMap { i => - (params.get(s"fileName-${i}"), params.get(s"content-${i}")) match { - case (Some(fileName), Some(content)) if(content.nonEmpty) => Some((if(fileName.isEmpty) s"gistfile${i + 1}.txt" else fileName, content)) - case _ => None - } - } - } else { - (0 to count - 1).map { i => - val fileName = request.getParameter(s"fileName-${i}") - val content = request.getParameter(s"content-${i}") - (if(fileName.isEmpty) s"gistfile${i + 1}.txt" else fileName, content) + (0 to count - 1).flatMap { i => + (params.get(s"fileName-${i}"), params.get(s"content-${i}")) match { + case (Some(fileName), Some(content)) if(content.nonEmpty) => Some((if(fileName.isEmpty) s"gistfile${i + 1}.txt" else fileName, content)) + case _ => None } } } + private def loginUserGroups: Seq[String] = { + context.loginAccount.map { account => + getGroupsByUserName(account.userName) + }.getOrElse(Nil) + } + } diff --git a/src/main/scala/gitbucket/gist/model/Gist.scala b/src/main/scala/gitbucket/gist/model/Gist.scala index 72845e5..6f06b0c 100644 --- a/src/main/scala/gitbucket/gist/model/Gist.scala +++ b/src/main/scala/gitbucket/gist/model/Gist.scala @@ -1,7 +1,9 @@ package gitbucket.gist.model +import gitbucket.core.issues.milestones.html.milestone + trait GistComponent { self: gitbucket.core.model.Profile => - import profile.simple._ + import profile.api._ import self._ lazy val Gists = TableQuery[Gists] @@ -16,7 +18,7 @@ trait GistComponent { self: gitbucket.core.model.Profile => val originUserName = column[String]("ORIGIN_USER_NAME") val originRepositoryName = column[String]("ORIGIN_REPOSITORY_NAME") val mode = column[String]("MODE") - def * = (userName, repositoryName, title, description, registeredDate, updatedDate, originUserName.?, originRepositoryName.?, mode) <> (Gist.tupled, Gist.unapply) + def * = (userName, repositoryName, title, description, registeredDate, updatedDate, originUserName.?, originRepositoryName.?, mode).<>(Gist.tupled, Gist.unapply) } } @@ -33,15 +35,16 @@ case class Gist( ){ def toRepositoryInfo = { gitbucket.core.service.RepositoryService.RepositoryInfo( - owner = userName, - name = repositoryName, - repository = null, - issueCount = 0, - pullCount = 0, - forkedCount = 0, - branchList = Nil, - tags = Nil, - managers = Nil + owner = userName, + name = repositoryName, + repository = null, + issueCount = 0, + pullCount = 0, + forkedCount = 0, + milestoneCount = 0, + branchList = Nil, + tags = Nil, + managers = Nil ) } } diff --git a/src/main/scala/gitbucket/gist/model/GistComment.scala b/src/main/scala/gitbucket/gist/model/GistComment.scala index c14b598..5265476 100644 --- a/src/main/scala/gitbucket/gist/model/GistComment.scala +++ b/src/main/scala/gitbucket/gist/model/GistComment.scala @@ -1,7 +1,7 @@ package gitbucket.gist.model trait GistCommentComponent { self: gitbucket.core.model.Profile => - import profile.simple._ + import profile.api._ import self._ lazy val GistComments = new TableQuery(tag => new GistComments(tag)){ @@ -16,7 +16,7 @@ trait GistCommentComponent { self: gitbucket.core.model.Profile => val content = column[String]("CONTENT") val registeredDate = column[java.util.Date]("REGISTERED_DATE") val updatedDate = column[java.util.Date]("UPDATED_DATE") - def * = (userName, repositoryName, commentId, commentedUserName, content, registeredDate, updatedDate) <> (GistComment.tupled, GistComment.unapply) + def * = (userName, repositoryName, commentId, commentedUserName, content, registeredDate, updatedDate).<>(GistComment.tupled, GistComment.unapply) } } diff --git a/src/main/scala/gitbucket/gist/service/GistCommentService.scala b/src/main/scala/gitbucket/gist/service/GistCommentService.scala index 816a2bf..2258482 100644 --- a/src/main/scala/gitbucket/gist/service/GistCommentService.scala +++ b/src/main/scala/gitbucket/gist/service/GistCommentService.scala @@ -3,7 +3,8 @@ package gitbucket.gist.service import scala.language.reflectiveCalls import gitbucket.gist.model.GistComment import gitbucket.gist.model.Profile._ -import profile.simple._ +import gitbucket.gist.model.Profile.profile.blockingApi._ +import gitbucket.gist.model.Profile.dateColumnType trait GistCommentService { diff --git a/src/main/scala/gitbucket/gist/service/GistService.scala b/src/main/scala/gitbucket/gist/service/GistService.scala index a2658fb..5ef7229 100644 --- a/src/main/scala/gitbucket/gist/service/GistService.scala +++ b/src/main/scala/gitbucket/gist/service/GistService.scala @@ -4,39 +4,39 @@ import gitbucket.core.model.Account import gitbucket.gist.model.Gist import gitbucket.gist.model.Mode import gitbucket.gist.model.Profile._ -import profile.simple._ +import gitbucket.gist.model.Profile.profile.blockingApi._ +import gitbucket.gist.model.Profile.dateColumnType trait GistService { - def getRecentGists(userName: String, offset: Int, limit: Int)(implicit s: Session): Seq[Gist] = - Gists.filter(_.userName === userName.bind).sortBy(_.registeredDate desc).drop(offset).take(limit).list + def getVisibleGists(offset: Int, limit: Int, account: Option[Account])(implicit s: Session): Seq[Gist] = + visibleHistsQuery(account).sortBy(_.registeredDate desc).drop(offset).take(limit).list - def getVisibleGists(offset: Int, limit: Int, account: Option[Account])(implicit s: Session): Seq[Gist] = { - val query = account.map { x => + def countVisibleGists(account: Option[Account])(implicit s: Session): Int = + Query(visibleHistsQuery(account).length).first + + private def visibleHistsQuery(account: Option[Account]): Query[Gists, Gists#TableElementType, Seq] = { + account.map { x => Gists.filter { t => (t.mode === "PUBLIC".bind) || (t.userName === x.userName.bind) } } getOrElse { Gists.filter { t => (t.mode === "PUBLIC".bind) } } - query.sortBy(_.registeredDate desc).drop(offset).take(limit).list } - def countPublicGists()(implicit s: Session): Int = - Query(Gists.filter(_.mode === "PUBLIC".bind).length).first - def getUserGists(userName: String, loginUserName: Option[String], offset: Int, limit: Int)(implicit s: Session): Seq[Gist] = - (if(loginUserName.isDefined){ - Gists filter(t => (t.userName === userName.bind) && ((t.userName === loginUserName.bind) || (t.mode === "PUBLIC".bind))) - } else { - Gists filter(t => (t.userName === userName.bind) && (t.mode === "PUBLIC".bind)) - }).sortBy(_.registeredDate desc).drop(offset).take(limit).list + userGistsQuery(userName, loginUserName).sortBy(_.registeredDate desc).drop(offset).take(limit).list def countUserGists(userName: String, loginUserName: Option[String])(implicit s: Session): Int = - Query((if(loginUserName.isDefined){ - Gists.filter(t => (t.userName === userName.bind) && ((t.userName === loginUserName.bind) || (t.mode === "PUBLIC".bind))) + Query(userGistsQuery(userName, loginUserName).length).first + + private def userGistsQuery(userName: String, loginUserName: Option[String]): Query[Gists, Gists#TableElementType, Seq] = { + if (loginUserName.isDefined) { + Gists filter (t => (t.userName === userName.bind) && ((t.userName === loginUserName.bind) || (t.mode === "PUBLIC".bind))) } else { - Gists.filter(t => (t.userName === userName.bind) && (t.mode === "PUBLIC".bind)) - }).length).first + Gists filter (t => (t.userName === userName.bind) && (t.mode === "PUBLIC".bind)) + } + } def getGist(userName: String, repositoryName: String)(implicit s: Session): Option[Gist] = Gists.filter(t => (t.userName === userName.bind) && (t.repositoryName === repositoryName.bind)).firstOption diff --git a/src/main/scala/gitbucket/gist/util/GistAuthenticator.scala b/src/main/scala/gitbucket/gist/util/GistAuthenticator.scala index 9db97b0..7c7bc99 100644 --- a/src/main/scala/gitbucket/gist/util/GistAuthenticator.scala +++ b/src/main/scala/gitbucket/gist/util/GistAuthenticator.scala @@ -1,26 +1,25 @@ package gitbucket.gist.util import gitbucket.core.controller.ControllerBase -import gitbucket.core.util.ControlUtil._ +import gitbucket.core.service.AccountService import gitbucket.core.util.Implicits._ /** * Allows only editor of the accessed snippet. */ -trait GistEditorAuthenticator { self: ControllerBase => +trait GistEditorAuthenticator { self: ControllerBase with AccountService => protected def editorOnly(action: => Any) = { authenticate(action) } protected def editorOnly[T](action: T => Any) = (form: T) => { authenticate(action(form)) } private def authenticate(action: => Any) = { { - defining(request.paths){ paths => - if(context.loginAccount.map { loginAccount => - loginAccount.isAdmin || loginAccount.userName == paths(1) - }.getOrElse(false)){ - action - } else { - Unauthorized() - } + val paths = request.paths + if(context.loginAccount.map { loginAccount => + loginAccount.isAdmin || loginAccount.userName == paths(1) || getGroupsByUserName(loginAccount.userName).contains(paths(1)) + }.getOrElse(false)){ + action + } else { + Unauthorized() } } } diff --git a/src/main/scala/gitbucket/gist/util/GistUtils.scala b/src/main/scala/gitbucket/gist/util/GistUtils.scala index 35f8c65..b5f7f93 100644 --- a/src/main/scala/gitbucket/gist/util/GistUtils.scala +++ b/src/main/scala/gitbucket/gist/util/GistUtils.scala @@ -12,9 +12,9 @@ import org.eclipse.jgit.lib.{FileMode, Constants, ObjectId} object GistUtils { - def isEditable(userName: String)(implicit context: Context): Boolean = { + def isEditable(userName: String, groupNames: Seq[String])(implicit context: Context): Boolean = { context.loginAccount.map { loginAccount => - loginAccount.isAdmin || loginAccount.userName == userName + loginAccount.isAdmin || loginAccount.userName == userName || groupNames.contains(userName) }.getOrElse(false) } @@ -38,7 +38,15 @@ object GistUtils { commitId } - def getLines(source: String): String = source.split("\n").take(10).mkString("\n") + def getLines(fileName: String, source: String): String = { + val lines = source.split("\n").map(_.trim).take(10) + + (if((fileName.endsWith(".md") || fileName.endsWith(".markdown")) && lines.count(_ == "```") % 2 != 0) { + lines :+ "```" + } else { + lines + }).mkString("\n") + } def isGistFile(fileName: String): Boolean = fileName.matches("gistfile[0-9]+\\.txt") @@ -48,11 +56,13 @@ object GistUtils { def httpUrl: String = s"${baseUrl}/git/gist/${gist.userName}/${gist.repositoryName}.git" - def sshUrl(loginUser: String): String = { - val host = """^https?://(.+?)(:\d+)?/""".r.findFirstMatchIn(httpUrl).get.group(1) - s"ssh://${loginUser}@${host}:${settings.sshPort.getOrElse(SystemSettingsService.DefaultSshPort)}/gist/${gist.userName}/${gist.repositoryName}.git" - } + def embedUrl: String = s"${baseUrl}/gist/${gist.userName}/${gist.repositoryName}.js" + def sshUrl: Option[String] = { + settings.sshUrl.map { sshUrl => + s"${sshUrl}/gist/${gist.userName}/${gist.repositoryName}.git" + } + } } } diff --git a/src/main/twirl/gitbucket/gist/commentedit.scala.html b/src/main/twirl/gitbucket/gist/commentedit.scala.html index 5b816fe..6a6da7a 100644 --- a/src/main/twirl/gitbucket/gist/commentedit.scala.html +++ b/src/main/twirl/gitbucket/gist/commentedit.scala.html @@ -1,9 +1,15 @@ -@(content: String, commentId: Int, userName: String, repoName: String)(implicit context: gitbucket.core.controller.Context) +@(gist: gitbucket.gist.model.Gist, content: String, commentId: Int)(implicit context: gitbucket.core.controller.Context) - -
- - +@gitbucket.gist.html.commentpreview( + gist = gist, + content = content, + style = "height: 100px; max-height: 150px;", + elastic = true, + uid = commentId +) +
+ +
- - - + + + + -} + $('#repository-url-http').click(function(){ + $('#repository-url-proto').text('HTTP'); + $('#repository-url').val('@repositoryUrl.httpUrl'); + $('#repository-url-copy').attr('data-clipboard-text', $('#repository-url').val()); + }); + @if(context.settings.ssh.enabled && context.loginAccount.isDefined){ + @repositoryUrl.sshUrl.map { sshUrl => + $('#repository-url-ssh').click(function(){ + $('#repository-url-proto').text('SSH'); + $('#repository-url').val('@sshUrl'); + $('#repository-url-copy').attr('data-clipboard-text', $('#repository-url').val()); + }); + } + } +}); + diff --git a/src/main/twirl/gitbucket/gist/profile.scala.html b/src/main/twirl/gitbucket/gist/profile.scala.html index 8f0b98b..ef3f630 100644 --- a/src/main/twirl/gitbucket/gist/profile.scala.html +++ b/src/main/twirl/gitbucket/gist/profile.scala.html @@ -1,7 +1,12 @@ -@(account: gitbucket.core.model.Account, groupNames: List[String], - gists: Seq[gitbucket.gist.model.Gist])(implicit context: gitbucket.core.controller.Context) +@(account: gitbucket.core.model.Account, groupNames: List[String], extraMailAddresses: List[String], + gists: Seq[gitbucket.gist.model.Gist], createSnippet: Boolean)(implicit context: gitbucket.core.controller.Context) @import gitbucket.gist.model.Mode -@gitbucket.core.account.html.main(account, groupNames, "snippets"){ +@gitbucket.core.account.html.main(account, groupNames, "snippets", extraMailAddresses){ + @if(createSnippet){ + + } @if(gists.isEmpty){ No snippets } else { diff --git a/src/main/twirl/gitbucket/gist/revisions.scala.html b/src/main/twirl/gitbucket/gist/revisions.scala.html index 54ca641..433765f 100644 --- a/src/main/twirl/gitbucket/gist/revisions.scala.html +++ b/src/main/twirl/gitbucket/gist/revisions.scala.html @@ -6,75 +6,77 @@ @import gitbucket.core.view.helpers @import org.eclipse.jgit.diff.DiffEntry.ChangeType @gitbucket.core.html.main(s"Revisions ยท ${gist.repositoryName}"){ - +
- @gitbucket.gist.html.header(gist, forkedCount, editable) -
- @gitbucket.gist.html.menu("revision", gist, repositoryUrl) -
- @revisions.map { case (revision, diffs) => -
-
- @helpers.avatar(revision, 20) @revision.authorName revised this @gitbucket.core.helper.html.datetimeago(revision.authorTime) -
- @if(diffs.isEmpty){ - No changes. - } - @revision.id.substring(0, 7) -
-
+
+ @gitbucket.gist.html.header(gist, forkedCount, editable) +
+ @gitbucket.gist.html.menu("revision", gist, repositoryUrl) +
+ @revisions.map { case (revision, diffs) =>
- @diffs.zipWithIndex.map { case (diff, i) => - - - - + +
- @diff.changeType match { - case ChangeType.ADD => { - - - @diff.newPath - } - case ChangeType.MODIFY => { - - - @diff.newPath - } - case ChangeType.DELETE => { - - - @diff.oldPath +
+ @helpers.avatar(revision, 20) @revision.authorName revised this @gitbucket.core.helper.html.datetimeago(revision.authorTime) +
+ @if(diffs.isEmpty){ + No changes. + } + @revision.id.substring(0, 7) +
+
+
+ @diffs.zipWithIndex.map { case (diff, i) => + + + + + + + + + - - - - - - -
+ @diff.changeType match { + case ChangeType.ADD => { + + + @diff.newPath + } + case ChangeType.MODIFY => { + + + @diff.newPath + } + case ChangeType.DELETE => { + + + @diff.oldPath + } + case _ => { + } } - case _ => { +
+ @if(diff.newContent != None || diff.oldContent != None){ +
+ + + } else { + Not supported } - } - -
- @if(diff.newContent != None || diff.oldContent != None){ -
- - - } else { - Not supported - } -
- } + +
+ } +
-
- } + } +
- - - + + +