Compare commits

...

63 Commits

Author SHA1 Message Date
Naoki Takezoe
8ab6b386a3 Release 4.45.0 (#3916) 2026-01-10 11:55:30 +09:00
Naoki Takezoe
2dd2c4f568 Deprecate helpers.markdown() (#3913) 2026-01-09 22:15:50 +09:00
Naoki Takezoe
36f7011ebf Fix warnings in PluginRegistry (#3914) 2026-01-09 21:46:28 +09:00
Yasumichi Akahoshi
fbcf962630 Check existence of Wiki page for Wiki links (#3907) 2026-01-09 11:22:51 +09:00
Naoki Takezoe
3b9b261878 Remove unused imports (#3912) 2026-01-09 08:58:48 +09:00
Naoki Takezoe
b334809e3e Add option to display fullname instead of username on UI (#3910) 2026-01-08 14:21:23 +09:00
Scala Steward
9a1324a870 Update logback-classic to 1.5.24 2026-01-07 09:10:47 +09:00
Scala Steward
b7c39e90d2 Update sbt, sbt-dependency-tree, ... to 1.12.0 2026-01-05 16:38:47 +09:00
Scala Steward
63b4f25687 Update oauth2-oidc-sdk to 11.31 2026-01-04 19:53:19 +09:00
Yasumichi Akahoshi
b582af4469 Fix bug that #3894 doesn't work with context path (#3906) 2026-01-03 20:54:42 +09:00
Yasumichi Akahoshi
ecd3f5b4eb Fix system menu of plugins (#3902)
In the system settings, the menu-item-hover class has been added to the plugin menu.
2025-12-31 20:41:12 +09:00
Yasumichi Akahoshi
7be433d331 Remove unnecessary CSS style (#3899) 2025-12-31 20:40:00 +09:00
Scala Steward
154be0f425 Update oauth2-oidc-sdk to 11.30.2 2025-12-30 20:17:02 +09:00
Scala Steward
9ddd2065b7 Update scalafmt-core to 3.10.3 2025-12-24 06:54:30 +09:00
Naoki Takezoe
6917cbf224 Fix typo in #3896 (#3898) 2025-12-23 11:48:06 +09:00
Naoki Takezoe
637b033782 Use Commons IO's IOUtils instead of Commons Compress's one (#3897) 2025-12-23 11:03:19 +09:00
Naoki Takezoe
7aee451a55 Include path in filePath param for preview in the online editor (#3896) 2025-12-23 10:59:08 +09:00
Scala Steward
e1e369d653 Update logback-classic to 1.5.23 2025-12-22 12:06:20 +09:00
Naoki Takezoe
c2ad66438c Preview based on the correct filename on the editor (#3894) 2025-12-21 13:23:30 +09:00
Yasumichi Akahoshi
c88e5adac2 Add support for an alternative renderer to commit comments, wikis, and issues. (#3882) 2025-12-21 12:17:34 +09:00
Scala Steward
703fb4a650 Update mariadb-java-client to 2.7.13 2025-12-18 07:31:16 +09:00
Scala Steward
465282580f Update testcontainers-mysql, ... to 2.0.3 2025-12-16 11:51:40 +09:00
dependabot[bot]
e041f8ffa0 Bump actions/upload-artifact from 5 to 6
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 5 to 6.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v5...v6)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-15 13:36:38 +09:00
dependabot[bot]
094386bb65 Bump actions/cache from 4 to 5
Bumps [actions/cache](https://github.com/actions/cache) from 4 to 5.
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](https://github.com/actions/cache/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/cache
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-15 13:36:25 +09:00
Scala Steward
b9e830f06e Update logback-classic to 1.5.22 2025-12-12 08:12:41 +09:00
Scala Steward
bdbfbc62a9 Update sbt-scoverage to 2.4.3 2025-12-11 07:25:38 +09:00
Scala Steward
5787a02daa Update mockito-core to 5.21.0 2025-12-10 07:02:13 +09:00
Yasumichi Akahoshi
0d6e5af8d7 Tentative fix for issue #2456 (#3883)
Co-authored-by: Naoki Takezoe <takezoe@gmail.com>
2025-12-07 01:26:37 +09:00
scala-steward-bot
88a8973685 Update ec4j-core to 1.2.0 (#3884) 2025-12-04 02:10:48 +09:00
kenji yoshida
e640cec323 Update CODEOWNERS 2025-11-30 08:52:00 +09:00
Scala Steward
5a4226db1d Update scalafmt-core to 3.10.2 2025-11-30 08:51:04 +09:00
dependabot[bot]
fb48c2a874 Bump actions/checkout from 5 to 6
Bumps [actions/checkout](https://github.com/actions/checkout) from 5 to 6.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v5...v6)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-24 12:11:07 +09:00
Scala Steward
28da703052 Update xz to 1.11 2025-11-20 06:01:50 +09:00
Scala Steward
2568171083 Update scala-library to 2.13.18 2025-11-18 07:33:52 +09:00
Scala Steward
2dff7f1b9e Update sbt-scoverage to 2.4.2 2025-11-18 07:33:40 +09:00
Scala Steward
8a79de8b50 Update testcontainers-mysql, ... to 2.0.2 2025-11-14 07:08:37 +09:00
Scala Steward
1b763c753c Update logback-classic to 1.5.21 2025-11-11 07:00:54 +09:00
Scala Steward
3bb1b1269c Update commons-io to 2.21.0 2025-11-08 07:23:38 +09:00
Scala Steward
dbbaf3cc9c Update scala3-library to 3.7.4 2025-11-08 07:01:23 +09:00
Scala Steward
4465a62b5f Update oauth2-oidc-sdk to 11.30.1 2025-11-07 07:19:19 +09:00
Scala Steward
fb48e75ecf Update sbt-scoverage to 2.4.1 2025-11-07 05:16:14 +09:00
Naoki Takezoe
a79142074f Fix community plugins link in README (#3867)
Updated the link to the GitBucket community plugins page.
2025-11-01 15:53:22 +09:00
Scala Steward
9f58c6dce7 Update sbt-scalafmt to 2.5.6 2025-10-31 07:27:37 +09:00
dependabot[bot]
6fe903afab Bump actions/upload-artifact from 4 to 5
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4 to 5.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-27 13:23:19 +09:00
Scala Steward
0d94865633 Update sbt-scoverage to 2.4.0 2025-10-22 08:07:20 +09:00
xuwei-k
2fbdead64b Update testcontainers 2025-10-20 07:59:52 +09:00
Scala Steward
72a354931e Update scalafmt-core to 3.10.1 2025-10-20 07:28:21 +09:00
Scala Steward
cb8affcd0d Update logback-classic to 1.5.20 2025-10-20 06:53:52 +09:00
scala-steward-bot
a5a997eb40 Update scalafmt-core to 3.10.0 (#3857) 2025-10-18 10:47:07 +09:00
Naoki Takezoe
b4220aab68 Test with MySQL 8.4 (#3860) 2025-10-18 10:40:16 +09:00
Scala Steward
c8a4798f86 Update oauth2-oidc-sdk to 11.30 2025-10-16 20:05:41 +09:00
xuwei-k
6b8e9e8892 add JDK 25 CI 2025-10-13 17:57:35 +09:00
xuwei-k
9ab9363d0b fix warnings 2025-10-13 17:56:55 +09:00
Scala Steward
1edb18a147 Update sbt, sbt-dependency-tree, ... to 1.11.7 2025-10-06 06:59:52 +09:00
Scala Steward
d8b4bf3033 Update thumbnailator to 0.4.21 2025-10-03 06:32:36 +09:00
Scala Steward
6597c4490b Update logback-classic to 1.5.19 2025-10-01 06:22:46 +09:00
Scala Steward
daaf1696ad Update oauth2-oidc-sdk to 11.29.2 2025-09-30 21:44:47 +09:00
Scala Steward
53a1ca7874 Update scala-library to 2.13.17 2025-09-30 18:43:32 +09:00
Scala Steward
f045fa4c6a Update h2 to 2.4.240 2025-09-25 06:58:37 +09:00
Scala Steward
53a7c5adf8 Update sbt-license-report to 1.9.0 2025-09-25 04:25:34 +09:00
takezoe
b142eca9a5 Update change log for 4.44.0 2025-09-23 09:56:19 +09:00
Naoki Takezoe
ba753a373b Fix release doc (#3844) 2025-09-23 09:46:49 +09:00
Naoki Takezoe
95ceca75a4 Update change log for 4.44.0 (#3845) 2025-09-23 09:46:35 +09:00
43 changed files with 437 additions and 288 deletions

1
.github/CODEOWNERS vendored
View File

@@ -3,3 +3,4 @@
build.sbt @xuwei-k
project/* @xuwei-k
.github/workflows/* @xuwei-k
.scalafmt.conf @xuwei-k

View File

@@ -9,11 +9,11 @@ jobs:
strategy:
fail-fast: false
matrix:
java: [17, 21]
java: [17, 25]
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- name: Cache
uses: actions/cache@v4
uses: actions/cache@v5
env:
cache-name: cache-sbt-libs
with:
@@ -36,7 +36,7 @@ jobs:
- name: Build executable
run: sbt executable
- name: Upload artifacts
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v6
with:
name: gitbucket-java${{ matrix.java }}-${{ github.sha }}
path: ./target/executable/gitbucket.*

View File

@@ -1,4 +1,4 @@
version = "3.9.10"
version = "3.10.3"
project.git = true
maxColumn = 120

View File

@@ -1,8 +1,15 @@
# Changelog
All changes to the project will be documented in this file.
## 4.45.0 - 11 Jan 2026
- Add new option to show full username on UI
- Support render plugin in issues, pull requests, wiki and commit comments
- Support link to other pages from Wiki page using Wiki link syntax
## 4.44.0 - 23 Sep 2025
- Enhanced branch protection which supports rejecting users fo push, etc.
- Enhanced branch protection which supports the following settings:
- Prevent pushes from non-allowed users
- Whether to apply restrictions to administrator users as well
- Improve logging for initialization errors
## 4.43.0 - 29 Jun 2025

View File

@@ -44,7 +44,7 @@ GitBucket has a plug-in system that allows extra functionality. Officially the f
- [gitbucket-pages-plugin](https://github.com/gitbucket/gitbucket-pages-plugin)
- [gitbucket-notifications-plugin](https://github.com/gitbucket/gitbucket-notifications-plugin)
You can find more plugins made by the community at [GitBucket community plugins](https://gitbucket-plugins.github.io/).
You can find more plugins made by the community at [GitBucket community plugins](https://github.com/gitbucket/gitbucket/wiki/Community-Plugins).
Building and Development
-----------
@@ -59,11 +59,12 @@ Support
- If you can't find same question and report, send it to our [Gitter chat room](https://gitter.im/gitbucket/gitbucket) before raising an issue.
- The highest priority of GitBucket is the ease of installation and API compatibility with GitHub, so your feature request might be rejected if they go against those principles.
What's New in 4.44.x
What's New in 4.45.x
-------------
## 4.44.0 - 23 Sep 2025
- Enhanced branch protection which supports rejecting users fo push, etc.
- Improve logging for initialization errors
## 4.45.0 - 11 Jan 2026
- Add new option to show full username on UI
- Support render plugin in issues, pull requests, wiki and commit comments
- Support link to other pages from Wiki page using Wiki link syntax
Note that you have to migrate h2 database file if you will upgrade GitBucket from 4.42 or before to 4.43 or later and you are using the default h2 database because h2 1.x and h2.x don't have compatibility: https://www.h2database.com/html/migration-to-v2.html

View File

@@ -2,7 +2,7 @@ import com.jsuereth.sbtpgp.PgpKeys._
val Organization = "io.github.gitbucket"
val Name = "gitbucket"
val GitBucketVersion = "4.44.0"
val GitBucketVersion = "4.45.0"
val ScalatraVersion = "3.1.2"
val JettyVersion = "10.0.26"
val JgitVersion = "6.10.1.202505221210-r"
@@ -14,9 +14,9 @@ sourcesInBase := false
organization := Organization
name := Name
version := GitBucketVersion
scalaVersion := "2.13.16"
scalaVersion := "2.13.18"
crossScalaVersions += "3.7.3"
crossScalaVersions += "3.7.4"
// scalafmtOnCompile := true
@@ -29,10 +29,10 @@ libraryDependencies ++= Seq(
"org.scalatra" %% "scalatra-json-javax" % ScalatraVersion,
"org.scalatra" %% "scalatra-forms-javax" % ScalatraVersion,
"org.json4s" %% "json4s-jackson" % "4.1.0-M8",
"commons-io" % "commons-io" % "2.20.0",
"commons-io" % "commons-io" % "2.21.0",
"io.github.gitbucket" % "solidbase" % "1.1.0",
"io.github.gitbucket" % "markedj" % "1.0.20",
"org.tukaani" % "xz" % "1.10",
"org.tukaani" % "xz" % "1.11",
"org.apache.commons" % "commons-compress" % "1.28.0",
"org.apache.commons" % "commons-email" % "1.6.0",
"commons-net" % "commons-net" % "3.12.0",
@@ -42,33 +42,32 @@ libraryDependencies ++= Seq(
"sshd-mina"
) exclude ("org.apache.sshd", "sshd-netty")
exclude ("org.apache.sshd", "sshd-spring-sftp"),
"org.apache.tika" % "tika-core" % "3.2.3",
"com.github.takezoe" %% "blocking-slick" % "0.0.14",
"com.novell.ldap" % "jldap" % "2009-10-07",
"com.h2database" % "h2" % "2.3.232",
"org.mariadb.jdbc" % "mariadb-java-client" % "2.7.12",
"org.postgresql" % "postgresql" % "42.7.8",
"ch.qos.logback" % "logback-classic" % "1.5.18",
"com.zaxxer" % "HikariCP" % "7.0.2" exclude ("org.slf4j", "slf4j-api"),
"com.typesafe" % "config" % "1.4.5",
"fr.brouillard.oss.security.xhub" % "xhub4j-core" % "1.1.0",
"io.github.java-diff-utils" % "java-diff-utils" % "4.16",
"org.cache2k" % "cache2k-all" % "1.6.0.Final",
"net.coobird" % "thumbnailator" % "0.4.20",
"com.github.zafarkhaja" % "java-semver" % "0.10.2",
"com.nimbusds" % "oauth2-oidc-sdk" % "11.29.1",
"org.eclipse.jetty" % "jetty-webapp" % JettyVersion % "provided",
"javax.servlet" % "javax.servlet-api" % "3.1.0" % "provided",
"junit" % "junit" % "4.13.2" % "test",
"org.scalatra" %% "scalatra-scalatest-javax" % ScalatraVersion % "test",
"org.mockito" % "mockito-core" % "5.20.0" % "test",
"com.dimafeng" %% "testcontainers-scala" % "0.43.0" % "test",
"org.testcontainers" % "mysql" % "1.21.3" % "test",
"org.testcontainers" % "postgresql" % "1.21.3" % "test",
"net.i2p.crypto" % "eddsa" % "0.3.0",
"is.tagomor.woothee" % "woothee-java" % "1.11.0",
"org.ec4j.core" % "ec4j-core" % "1.1.1",
"org.kohsuke" % "github-api" % "1.330" % "test"
"org.apache.tika" % "tika-core" % "3.2.3",
"com.github.takezoe" %% "blocking-slick" % "0.0.14",
"com.novell.ldap" % "jldap" % "2009-10-07",
"com.h2database" % "h2" % "2.4.240",
"org.mariadb.jdbc" % "mariadb-java-client" % "2.7.13",
"org.postgresql" % "postgresql" % "42.7.8",
"ch.qos.logback" % "logback-classic" % "1.5.24",
"com.zaxxer" % "HikariCP" % "7.0.2" exclude ("org.slf4j", "slf4j-api"),
"com.typesafe" % "config" % "1.4.5",
"fr.brouillard.oss.security.xhub" % "xhub4j-core" % "1.1.0",
"io.github.java-diff-utils" % "java-diff-utils" % "4.16",
"org.cache2k" % "cache2k-all" % "1.6.0.Final",
"net.coobird" % "thumbnailator" % "0.4.21",
"com.github.zafarkhaja" % "java-semver" % "0.10.2",
"com.nimbusds" % "oauth2-oidc-sdk" % "11.31",
"org.eclipse.jetty" % "jetty-webapp" % JettyVersion % "provided",
"javax.servlet" % "javax.servlet-api" % "3.1.0" % "provided",
"junit" % "junit" % "4.13.2" % "test",
"org.scalatra" %% "scalatra-scalatest-javax" % ScalatraVersion % "test",
"org.mockito" % "mockito-core" % "5.21.0" % "test",
"org.testcontainers" % "testcontainers-mysql" % "2.0.3" % "test",
"org.testcontainers" % "testcontainers-postgresql" % "2.0.3" % "test",
"net.i2p.crypto" % "eddsa" % "0.3.0",
"is.tagomor.woothee" % "woothee-java" % "1.11.0",
"org.ec4j.core" % "ec4j-core" % "1.2.0",
"org.kohsuke" % "github-api" % "1.330" % "test"
)
// Compiler settings

View File

@@ -38,7 +38,7 @@ Generate release files
For plug-in development, we have to publish the GitBucket jar file to the Maven central repository before release GitBucket itself.
First, start the sbt shell:
First, stage artifacts on your machine:
```bash
$ sbt publishSigned

View File

@@ -1 +1 @@
sbt.version=1.11.6
sbt.version=1.12.0

View File

@@ -1,11 +1,11 @@
scalacOptions ++= Seq("-unchecked", "-deprecation", "-feature")
addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.5.5")
addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.5.6")
addSbtPlugin("org.playframework.twirl" % "sbt-twirl" % "2.0.9")
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "2.3.1")
addSbtPlugin("org.scalatra.sbt" % "sbt-scalatra" % "1.0.4")
addSbtPlugin("com.github.sbt" % "sbt-pgp" % "2.3.1")
addSbtPlugin("com.github.sbt" % "sbt-license-report" % "1.8.0")
addSbtPlugin("org.scoverage" % "sbt-scoverage" % "2.3.1")
addSbtPlugin("com.github.sbt" % "sbt-license-report" % "1.9.0")
addSbtPlugin("org.scoverage" % "sbt-scoverage" % "2.4.3")
addDependencyTreePlugin

View File

@@ -1,4 +1,4 @@
notifications:1.11.0
gist:4.23.0
gist:4.24.0
emoji:4.6.0
pages:1.10.0

View File

@@ -121,7 +121,8 @@ object GitBucketCoreModule
new Version("4.42.0", new LiquibaseMigration("update/gitbucket-core_4.42.xml")),
new Version("4.42.1"),
new Version("4.43.0"),
new Version("4.44.0", new LiquibaseMigration("update/gitbucket-core_4.44.xml"))
new Version("4.44.0", new LiquibaseMigration("update/gitbucket-core_4.44.xml")),
new Version("4.45.0")
) {
java.util.logging.Logger.getLogger("liquibase").setLevel(Level.SEVERE)
}

View File

@@ -351,7 +351,7 @@ case class Context(
val path: String = settings.baseUrl.getOrElse(request.getContextPath)
val currentPath: String = request.getRequestURI.substring(request.getContextPath.length)
val baseUrl: String = settings.baseUrl(request)
val host: String = new java.net.URL(baseUrl).getHost
val host: String = new java.net.URI(baseUrl).toURL.getHost
val platform: String = request.getHeader("User-Agent") match {
case null => null
case agent if agent.contains("Mac") => "mac"

View File

@@ -13,8 +13,8 @@ import gitbucket.core.util.*
import gitbucket.core.view.helpers.*
import org.scalatra.Ok
import org.scalatra.forms.*
import gitbucket.core.service.ActivityService.*
import gitbucket.core.view.helpers
class IndexController
extends IndexControllerBase
@@ -92,6 +92,10 @@ trait IndexControllerBase extends ControllerBase {
}
}
get("/_is_renderable") {
helpers.isRenderable(params("filename"))
}
get("/signin") {
if (context.loginAccount.nonEmpty) {
redirect("/")
@@ -247,13 +251,22 @@ trait IndexControllerBase extends ControllerBase {
}
}
.map { t =>
Map(
"label" -> s"${avatar(t.userName, 16)}<b>@${StringUtil.escapeHtml(
StringUtil.cutTail(t.userName, 25, "...")
)}</b> ${StringUtil
.escapeHtml(StringUtil.cutTail(t.fullName, 25, "..."))}",
"value" -> t.userName
)
if (t.isGroupAccount) {
Map(
"label" -> s"${avatar(t.userName, 16)} <b>@${StringUtil.escapeHtml(
StringUtil.cutTail(t.userName, 25, "...")
)}</b>",
"value" -> t.userName
)
} else {
Map(
"label" -> s"${avatar(t.userName, 16)} <b>@${StringUtil.escapeHtml(
StringUtil.cutTail(t.userName, 25, "...")
)}</b> (${StringUtil
.escapeHtml(StringUtil.cutTail(t.fullName, 25, "..."))})",
"value" -> t.userName
)
}
}
)
)

View File

@@ -6,8 +6,7 @@ import gitbucket.core.service.IssuesService.*
import gitbucket.core.service.*
import gitbucket.core.util.Implicits.*
import gitbucket.core.util.*
import gitbucket.core.view
import gitbucket.core.view.Markdown
import gitbucket.core.view.helpers
import org.scalatra.forms.*
import org.scalatra.{BadRequest, Ok}
@@ -271,20 +270,24 @@ trait IssuesControllerBase extends ControllerBase {
case t if t == "html" => html.editissue(x.content, x.issueId, repository)
} getOrElse {
contentType = formats("json")
val content = helpers
.renderMarkup(
filePath = List("temporary.md"),
fileContent = x.content getOrElse "No description given.",
branch = repository.repository.defaultBranch,
repository = repository,
enableWikiLink = false,
enableRefsLink = true,
enableAnchor = true,
enableLineBreaks = true,
enableTaskList = true,
hasWritePermission = true
)
.toString()
org.json4s.jackson.Serialization.write(
Map(
"title" -> x.title,
"content" -> Markdown.toHtml(
markdown = x.content getOrElse "No description given.",
repository = repository,
branch = repository.repository.defaultBranch,
enableWikiLink = false,
enableRefsLink = true,
enableAnchor = true,
enableLineBreaks = true,
enableTaskList = true,
hasWritePermission = true
)
"content" -> content
)
)
}
@@ -301,19 +304,23 @@ trait IssuesControllerBase extends ControllerBase {
case t if t == "html" => html.editcomment(x.content, x.commentId, repository)
} getOrElse {
contentType = formats("json")
val content = helpers
.renderMarkup(
filePath = List("temporary.md"),
fileContent = x.content,
branch = repository.repository.defaultBranch,
repository = repository,
enableWikiLink = false,
enableRefsLink = true,
enableAnchor = true,
enableLineBreaks = true,
enableTaskList = true,
hasWritePermission = true
)
.toString()
org.json4s.jackson.Serialization.write(
Map(
"content" -> view.Markdown.toHtml(
markdown = x.content,
repository = repository,
branch = repository.repository.defaultBranch,
enableWikiLink = false,
enableRefsLink = true,
enableAnchor = true,
enableLineBreaks = true,
enableTaskList = true,
hasWritePermission = true
)
"content" -> content
)
)
}

View File

@@ -23,8 +23,7 @@ import org.apache.commons.compress.archivers.zip.{ZipArchiveEntry, ZipArchiveOut
import org.apache.commons.compress.compressors.bzip2.BZip2CompressorOutputStream
import org.apache.commons.compress.compressors.gzip.GzipCompressorOutputStream
import org.apache.commons.compress.compressors.xz.XZCompressorOutputStream
import org.apache.commons.compress.utils.IOUtils
import org.apache.commons.io.FileUtils
import org.apache.commons.io.{FileUtils, IOUtils}
import org.scalatra.forms.*
import org.eclipse.jgit.api.{ArchiveCommand, Git}
import org.eclipse.jgit.archive.{TgzFormat, ZipFormat}
@@ -35,7 +34,7 @@ import org.eclipse.jgit.treewalk.TreeWalk.OperationType
import org.eclipse.jgit.treewalk.filter.PathFilter
import org.eclipse.jgit.util.io.EolStreamTypeUtil
import org.json4s.jackson.Serialization
import org.scalatra._
import org.scalatra.*
import org.scalatra.i18n.Messages
class RepositoryViewerController
@@ -170,31 +169,19 @@ trait RepositoryViewerControllerBase extends ControllerBase {
*/
post("/:owner/:repository/_preview")(referrersOnly { repository =>
contentType = "text/html"
val filename = params.get("filename")
filename match {
case Some(f) =>
helpers.renderMarkup(
filePath = List(f),
fileContent = params("content"),
branch = repository.repository.defaultBranch,
repository = repository,
enableWikiLink = params("enableWikiLink").toBoolean,
enableRefsLink = params("enableRefsLink").toBoolean,
enableAnchor = false
)
case None =>
helpers.markdown(
markdown = params("content"),
repository = repository,
branch = repository.repository.defaultBranch,
enableWikiLink = params("enableWikiLink").toBoolean,
enableRefsLink = params("enableRefsLink").toBoolean,
enableLineBreaks = params("enableLineBreaks").toBoolean,
enableTaskList = params("enableTaskList").toBoolean,
enableAnchor = false,
hasWritePermission = hasDeveloperRole(repository.owner, repository.name, context.loginAccount)
)
}
val filename = params.get("filename").getOrElse("temporary.md")
helpers.renderMarkup(
filePath = filename.split("/").toList,
fileContent = params("content"),
branch = repository.repository.defaultBranch,
repository = repository,
enableWikiLink = params("enableWikiLink").toBoolean,
enableRefsLink = params("enableRefsLink").toBoolean,
enableLineBreaks = params("enableLineBreaks").toBoolean,
enableTaskList = params("enableTaskList").toBoolean,
enableAnchor = false,
hasWritePermission = hasDeveloperRole(repository.owner, repository.name, context.loginAccount)
)
})
/**
@@ -897,19 +884,23 @@ trait RepositoryViewerControllerBase extends ControllerBase {
case t if t == "html" => html.editcomment(x.content, x.commentId, repository)
} getOrElse {
contentType = formats("json")
val content = helpers
.renderMarkup(
filePath = List("temporary.md"),
fileContent = x.content,
branch = repository.repository.defaultBranch,
repository = repository,
enableWikiLink = false,
enableRefsLink = true,
enableAnchor = true,
enableLineBreaks = true,
enableTaskList = true,
hasWritePermission = true
)
.toString()
org.json4s.jackson.Serialization.write(
Map(
"content" -> view.Markdown.toHtml(
markdown = x.content,
repository = repository,
branch = repository.repository.defaultBranch,
enableWikiLink = false,
enableRefsLink = true,
enableAnchor = true,
enableLineBreaks = true,
enableTaskList = true,
hasWritePermission = true
)
"content" -> content
)
)
}

View File

@@ -125,7 +125,8 @@ trait SystemSettingsControllerBase extends AccountManagementControllerBase {
"maxDiffFiles" -> trim(label("Max diff files", number(required))),
"maxDiffLines" -> trim(label("Max diff lines", number(required)))
)(RepositoryViewerSettings.apply),
"defaultBranch" -> trim(label("Default branch", text(required)))
"defaultBranch" -> trim(label("Default branch", text(required))),
"showFullName" -> trim(label("Show full name", boolean()))
)(SystemSettings.apply).verifying { settings =>
Vector(
if (settings.ssh.enabled && settings.baseUrl.isEmpty) {

View File

@@ -5,7 +5,7 @@ import gitbucket.core.service.{AccountService, ProtectedBranchService, Repositor
import gitbucket.core.util.*
import gitbucket.core.util.Directory.*
import gitbucket.core.util.Implicits.*
import gitbucket.core.util.JGitUtil.{getBranchesNoMergeInfo, processTree}
import gitbucket.core.util.JGitUtil.getBranchesNoMergeInfo
import org.eclipse.jgit.api.Git
import org.scalatra.NoContent

View File

@@ -90,7 +90,17 @@ trait ApiRepositoryContentsControllerBase extends ControllerBase {
path,
"\" id=\"file\">",
"<article>",
renderMarkup(path.split("/").toList, new String(c), refStr, repository, false, false, true).body,
renderMarkup(
filePath = path.split("/").toList,
fileContent = new String(c),
branch = refStr,
repository = repository,
enableWikiLink = false,
enableRefsLink = false,
enableAnchor = false,
enableLineBreaks = true,
enableTaskList = true
).body,
"</article>",
"</div>"
).mkString

View File

@@ -15,7 +15,7 @@ import gitbucket.core.service.RepositoryService.RepositoryInfo
import gitbucket.core.service.SystemSettingsService
import gitbucket.core.service.SystemSettingsService.SystemSettings
import gitbucket.core.util.{ConfigUtil, DatabaseConfig}
import gitbucket.core.util.Directory._
import gitbucket.core.util.Directory.*
import io.github.gitbucket.solidbase.Solidbase
import io.github.gitbucket.solidbase.manager.JDBCVersionManager
import io.github.gitbucket.solidbase.model.Module
@@ -25,7 +25,7 @@ import org.apache.sshd.server.command.Command
import org.slf4j.LoggerFactory
import play.twirl.api.Html
import scala.jdk.CollectionConverters._
import scala.jdk.CollectionConverters.*
class PluginRegistry {
@@ -233,29 +233,29 @@ object PluginRegistry {
override def accept(dir: File, name: String): Boolean = name.endsWith(".jar")
})
.toSeq
.sortBy(x => Version.valueOf(getPluginVersion(x.getName)))
.sortBy(x => Version.parse(getPluginVersion(x.getName)))
.reverse
}
lazy val extraPluginDir: Option[String] = ConfigUtil.getConfigValue[String]("gitbucket.pluginDir")
private lazy val extraPluginDir: Option[String] = ConfigUtil.getConfigValue[String]("gitbucket.pluginDir")
def getGitBucketVersion(pluginJarFileName: String): Option[String] = {
val regex = ".+-gitbucket\\_(\\d+\\.\\d+\\.\\d+(-SNAPSHOT)?)-.+".r
private def getGitBucketVersion(pluginJarFileName: String): Option[String] = {
val regex = ".+-gitbucket_(\\d+\\.\\d+\\.\\d+(-SNAPSHOT)?)-.+".r
pluginJarFileName match {
case regex(all, _) => Some(all)
case _ => None
}
}
def getPluginVersion(pluginJarFileName: String): String = {
private def getPluginVersion(pluginJarFileName: String): String = {
val regex = ".+-((\\d+)\\.(\\d+)(\\.(\\d+))?(-SNAPSHOT)?)\\.jar$".r
pluginJarFileName match {
case regex(all, major, minor, _, patch, modifier) => {
if (patch != null) all
else {
case regex(all, major, minor, _, patch, modifier) =>
if (patch != null) {
all
} else {
s"${major}.${minor}.0" + (if (modifier == null) "" else modifier)
}
}
case _ => "0.0.0"
}
}
@@ -295,11 +295,10 @@ object PluginRegistry {
// Check duplication
instance.getPlugins().find(_.pluginId == pluginId) match {
case Some(x) => {
case Some(x) =>
logger.warn(s"Plugin ${pluginId} is duplicated. ${x.pluginJar.getName} is available.")
classLoader.close()
}
case None => {
case None =>
// Migration
val solidbase = new Solidbase()
solidbase
@@ -334,7 +333,6 @@ object PluginRegistry {
classLoader = classLoader
)
)
}
}
} catch {
case e: Throwable =>
@@ -369,9 +367,8 @@ object PluginRegistry {
extraWatcher = null
}
} catch {
case e: Exception => {
case e: Exception =>
logger.error(s"Error during plugin shutdown: ${plugin.pluginJar.getName}", e)
}
} finally {
plugin.classLoader.close()
}
@@ -437,7 +434,7 @@ class PluginWatchThread(context: ServletContext, dir: String) extends Thread wit
logger.info("Start PluginWatchThread: " + path)
try {
while (watchKey.isValid()) {
while (watchKey.isValid) {
val detectedWatchKey = watcher.take()
val events = detectedWatchKey.pollEvents.asScala.filter { e =>
e.context.toString != ".installed" && !e.context.toString.endsWith(".bak")

View File

@@ -20,7 +20,7 @@ trait Renderer {
object MarkdownRenderer extends Renderer {
override def render(request: RenderRequest): Html = {
import request._
import request.*
Html(
Markdown.toHtml(
markdown = fileContent,
@@ -29,9 +29,9 @@ object MarkdownRenderer extends Renderer {
enableWikiLink = enableWikiLink,
enableRefsLink = enableRefsLink,
enableAnchor = enableAnchor,
enableLineBreaks = false,
enableTaskList = true,
hasWritePermission = false
enableLineBreaks = enableLineBreaks,
enableTaskList = enableTaskList,
hasWritePermission = hasWritePermission
)(context)
)
}
@@ -51,5 +51,8 @@ case class RenderRequest(
enableWikiLink: Boolean,
enableRefsLink: Boolean,
enableAnchor: Boolean,
enableLineBreaks: Boolean,
enableTaskList: Boolean,
hasWritePermission: Boolean,
context: Context
)

View File

@@ -93,6 +93,7 @@ trait SystemSettingsService {
props.setProperty(RepositoryViewerMaxDiffFiles, settings.repositoryViewer.maxDiffFiles.toString)
props.setProperty(RepositoryViewerMaxDiffLines, settings.repositoryViewer.maxDiffLines.toString)
props.setProperty(DefaultBranch, settings.defaultBranch)
props.setProperty(ShowFullName, settings.showFullName.toString)
Using.resource(new java.io.FileOutputStream(GitBucketConf)) { out =>
props.store(out, null)
@@ -211,7 +212,8 @@ trait SystemSettingsService {
getValue(props, RepositoryViewerMaxDiffFiles, 100),
getValue(props, RepositoryViewerMaxDiffLines, 1000)
),
getValue(props, DefaultBranch, "main")
getValue(props, DefaultBranch, "main"),
getValue(props, ShowFullName, false)
)
}
}
@@ -238,7 +240,8 @@ object SystemSettingsService {
webHook: WebHook,
upload: Upload,
repositoryViewer: RepositoryViewerSettings,
defaultBranch: String
defaultBranch: String,
showFullName: Boolean
) {
def baseUrl(request: HttpServletRequest): String =
baseUrl.getOrElse(parseBaseUrl(request)).stripSuffix("/")
@@ -457,6 +460,7 @@ object SystemSettingsService {
private val RepositoryViewerMaxDiffFiles = "repository_viewer_max_diff_files"
private val RepositoryViewerMaxDiffLines = "repository_viewer_max_diff_lines"
private val DefaultBranch = "default_branch"
private val ShowFullName = "show_full_name"
private def getValue[A: ClassTag](props: java.util.Properties, key: String, default: A): A = {
getConfigValue(key).getOrElse {

View File

@@ -265,7 +265,7 @@ trait WebHookService {
}
private def validateTargetAddress(settings: SystemSettings, url: String): Boolean = {
val host = new java.net.URL(url).getHost
val host = new java.net.URI(url).toURL.getHost
!settings.webHook.blockPrivateAddress ||
!HttpClientUtil.isPrivateAddress(host) ||

View File

@@ -47,18 +47,28 @@ trait AvatarImageProvider { self: RequestCache =>
}
}
val displayName = if (!context.settings.showFullName) {
s"@$userName"
} else {
if (mailAddress.isEmpty) {
getAccountByUserNameFromCache(userName).map(_.fullName).getOrElse(s"@$userName")
} else {
getAccountByMailAddressFromCache(mailAddress).map(_.fullName).getOrElse(s"@$userName")
}
}
if (tooltip) {
Html(
s"""<img src="${src}" class="${if (size > 20) { "avatar" }
else { "avatar-mini" }}" style="width: ${size}px; height: ${size}px;"
| alt="@${StringUtil.escapeHtml(userName)}"
| data-toggle="tooltip" title="${StringUtil.escapeHtml(userName)}" />""".stripMargin
| alt="${StringUtil.escapeHtml(displayName)}"
| data-toggle="tooltip" title="${StringUtil.escapeHtml(displayName)}" />""".stripMargin
)
} else {
Html(
s"""<img src="${src}" class="${if (size > 20) { "avatar" }
else { "avatar-mini" }}" style="width: ${size}px; height: ${size}px;"
| alt="@${StringUtil.escapeHtml(userName)}" />""".stripMargin
| alt="${StringUtil.escapeHtml(displayName)}" />""".stripMargin
)
}
}

View File

@@ -9,6 +9,7 @@ import gitbucket.core.service.{RepositoryService, RequestCache}
import gitbucket.core.util.StringUtil
import io.github.gitbucket.markedj._
import io.github.gitbucket.markedj.Utils._
import gitbucket.core.service.WikiService
object Markdown {
@@ -75,7 +76,8 @@ object Markdown {
)(implicit val context: Context)
extends Renderer(options)
with LinkConverter
with RequestCache {
with RequestCache
with WikiService {
override def heading(text: String, level: Int, raw: String): String = {
val id = generateAnchorName(text)
@@ -192,7 +194,16 @@ object Markdown {
.stripSuffix(".git") + "/blob/" + branch + "/" + urlWithRawParam
}
} else {
repository.httpUrl.replaceFirst("/git/", "/").stripSuffix(".git") + "/wiki/_blob/" + url
// URL is being modified to link to the image file on the repository, but users may want to link to the page if the page name is a link.
// If the wiki page cannot be retrieved from the url, the blob address is returned; otherwise, the page address is returned.
val pathElems = context.currentPath.split("/")
val owner = pathElems(1)
val repos = pathElems(2)
if (getWikiPage(owner, repos, url, branch) == None) {
repository.httpUrl.replaceFirst("/git/", "/").stripSuffix(".git") + "/wiki/_blob/" + url
} else {
repository.httpUrl.replaceFirst("/git/", "/").stripSuffix(".git") + "/wiki/" + url
}
}
}
}

View File

@@ -101,6 +101,7 @@ object helpers extends AvatarImageProvider with LinkConverter with RequestCache
/**
* Converts Markdown of Wiki pages to HTML.
*/
@deprecated("This doesn't apply render plugins. Should use renderMarkup() instead.", "4.45.0")
def markdown(
markdown: String,
repository: RepositoryService.RepositoryInfo,
@@ -139,14 +140,29 @@ object helpers extends AvatarImageProvider with LinkConverter with RequestCache
repository: RepositoryService.RepositoryInfo,
enableWikiLink: Boolean,
enableRefsLink: Boolean,
enableAnchor: Boolean
enableAnchor: Boolean,
enableLineBreaks: Boolean,
enableTaskList: Boolean,
hasWritePermission: Boolean = false
)(implicit context: Context): Html = {
val fileName = filePath.last.toLowerCase
val extension = FileUtil.getExtension(fileName)
val renderer = PluginRegistry().getRenderer(extension)
renderer.render(
RenderRequest(filePath, fileContent, branch, repository, enableWikiLink, enableRefsLink, enableAnchor, context)
RenderRequest(
filePath,
fileContent,
branch,
repository,
enableWikiLink,
enableRefsLink,
enableAnchor,
enableLineBreaks,
enableTaskList,
hasWritePermission,
context
)
)
}
@@ -316,12 +332,30 @@ object helpers extends AvatarImageProvider with LinkConverter with RequestCache
*/
def assets(path: String)(implicit context: Context): String = s"${context.path}/assets${path}?${hashQuery}"
def displayUserName(userName: String, mailAddress: String = "")(implicit context: Context): String = {
val displayName = if (!context.settings.showFullName) {
userName
} else {
if (mailAddress.isEmpty) {
getAccountByUserNameFromCache(userName).map(_.fullName).getOrElse(userName)
} else {
getAccountByMailAddressFromCache(mailAddress).map(_.fullName).getOrElse(userName)
}
}
if (userName == displayName) {
userName
} else {
s"$userName ($displayName)"
}
}
/**
* Generates the text link to the account page.
* If user does not exist or disabled, this method returns user name as text without link.
*/
def user(userName: String, mailAddress: String = "", styleClass: String = "")(implicit context: Context): Html =
userWithContent(userName, mailAddress, styleClass)(Html(StringUtil.escapeHtml(userName)))
userWithContent(userName, mailAddress, styleClass)(Html(StringUtil.escapeHtml(displayUserName(userName, mailAddress))))
/**
* Generates the avatar link to the account page.
@@ -511,6 +545,18 @@ object helpers extends AvatarImageProvider with LinkConverter with RequestCache
Html(sb.toString())
}
/**
* Utility method to enable checkboxes
*/
def enableCheckbox(html: Html, enable: Boolean): Html = {
if (enable) {
val re = "(<input\\s+[^<>]*type=\"checkbox\"\\s+[^<>]*)\\s+disabled[^<>]*>".r
Html(re.replaceAllIn(html.toString(), "$1>"))
} else {
html
}
}
case class CommentDiffLine(newLine: Option[String], oldLine: Option[String], `type`: String, text: String)
def appendQueryString(baseUrl: String, queryString: String): String = {

View File

@@ -56,16 +56,16 @@ $(function(){
$('#addMember').click(function(){
$('#error-members').text('');
var userName = $('#memberName').val();
const userName = $('#memberName').val();
// check empty
if($.trim(userName) == ''){
if($.trim(userName) === ''){
return false;
}
// check duplication
var exists = $('#member-list li').filter(function(){
return $(this).data('name') == userName;
const exists = $('#member-list li').filter(function(){
return $(this).data('name') === userName;
}).length > 0;
if(exists){
$('#error-members').text('User has been already added.');
@@ -75,7 +75,7 @@ $(function(){
// check existence
$.post('@context.path/_user/existence', { 'userName': userName },
function(data, status){
if(data == 'user'){
if(data === 'user'){
addMemberHTML(userName, false);
$('#memberName').val('');
} else {
@@ -90,7 +90,7 @@ $(function(){
// Don't submit form by ENTER key
$('#memberName').keypress(function(e){
return !(e.keyCode == 13);
return !(e.keyCode === 13);
});
$('#delete').click(function(){
@@ -102,11 +102,11 @@ $(function(){
}
function addMemberHTML(userName, isManager){
var memberButton = $('<label class="btn btn-default btn-mini"><input type="radio" value="false" name="' + userName + '">Member</label>');
const memberButton = $('<label class="btn btn-default btn-mini"><input type="radio" value="false" name="' + userName + '">Member</label>');
if(!isManager){
memberButton.addClass('active');
}
var managerButton = $('<label class="btn btn-default btn-mini"><input type="radio" value="true" name="' + userName + '">Manager</label>');
const managerButton = $('<label class="btn btn-default btn-mini"><input type="radio" value="true" name="' + userName + '">Manager</label>');
if(isManager){
managerButton.addClass('active');
}
@@ -123,8 +123,8 @@ $(function(){
}
function updateMembers(){
var members = $('#member-list li').map(function(i, e){
var userName = $(e).data('name');
const members = $('#member-list li').map(function(i, e){
const userName = $(e).data('name');
return userName + ':' + $(e).find('label.active input[type=radio]').attr('value');
}).get().join(',');
$('#members').val(members);

View File

@@ -33,7 +33,7 @@
</li>
@gitbucket.core.plugin.PluginRegistry().getSystemSettingMenus.map { menu =>
@menu(context).map { link =>
<li@if(active==link.id){ class="active"}>
<li class="menu-item-hover @if(active==link.id){active}">
<a href="@context.path/@link.path">
<i class="menu-icon octicon octicon-@link.icon.getOrElse("plug")"></i>
<span>@link.label</span>

View File

@@ -253,6 +253,21 @@
<input type="radio" name="showMailAddress" value="false"@if(!context.settings.showMailAddress){ checked}>
<span class="strong">Hide</span> <span class="normal">- Hide mail address in user's profile page.</span>
</label>
</fieldset>
<!--====================================================================-->
<!-- Show full name -->
<!--====================================================================-->
<hr>
<label class="strong">Username display on UI</label>
<fieldset>
<label class="radio">
<input type="radio" name="showFullName" value="false"@if(!context.settings.showFullName){ checked}>
<span class="strong">User name</span> <span class="normal">- Username is primarily displayed on UI.</span>
</label>
<label class="radio">
<input type="radio" name="showFullName" value="true"@if(context.settings.showFullName){ checked}>
<span class="strong">Full name</span> <span class="normal">- Fullname is primarily displayed instead of username on UI.</span>
</label>
</fieldset>
<!--====================================================================-->
<!-- File upload -->

View File

@@ -25,20 +25,23 @@
<a href="@context.path/admin/users/@account.userName/_edituser">Edit</a>
}
</div>
<div class="strong">
@helpers.avatarLink(account.userName, 20)
@if(account.isRemoved){
@account.userName
} else {
<a href="@helpers.url(account.userName)">@account.userName</a>
}
@if(account.isGroupAccount){
(Group)
} else {
@if(account.isAdmin){
(Administrator)
<div>
<span class="strong">
@helpers.avatarLink(account.userName, 20)
@if(account.isRemoved){
@account.userName
} else {
(Normal)
<a href="@helpers.url(account.userName)">@account.userName</a>
}
</span>
@if(account.isGroupAccount){
- Group
} else {
(@account.fullName)
@if(account.isAdmin){
- Administrator
} else {
- Normal
}
}
@if(account.isGroupAccount){
@@ -50,10 +53,10 @@
<div>
<hr>
@if(!account.isGroupAccount){
<i class="octicon octicon-mail"></i> @account.mailAddress
<i class="octicon octicon-mail"></i>@account.mailAddress
}
@account.url.map { url =>
<i class="octicon octicon-home"></i> @url
<i class="octicon octicon-home"></i>@url
}
</div>
<div>

View File

@@ -21,16 +21,18 @@
</span>
</div>
<div class="commit-commentContent-@comment.commentId">
@helpers.markdown(
markdown = comment.content,
repository = repository,
branch = repository.repository.defaultBranch,
enableWikiLink = false,
enableRefsLink = true,
enableLineBreaks = true,
enableTaskList = true,
hasWritePermission = hasWritePermission
)
@helpers.renderMarkup(
filePath = List("temporary.md"),
fileContent = comment.content,
branch = repository.repository.defaultBranch,
repository = repository,
enableWikiLink = false,
enableRefsLink = true,
enableAnchor = true,
enableLineBreaks = true,
enableTaskList = true,
hasWritePermission = hasWritePermission
)
</div>
</div>
</div>

View File

@@ -57,6 +57,7 @@ $(function(){
$('#preview@uid').click(function(){
$('#preview-area@uid').html('<img src="@helpers.assets("/common/images/indicator.gif")"> Previewing...');
$.post('@helpers.url(repository)/_preview', {
filename : "temporary.md",
content : $('#content@uid').val(),
enableWikiLink : @enableWikiLink,
enableRefsLink : @enableRefsLink,

View File

@@ -24,7 +24,7 @@
</a>
</span>
@if(comment.action != "commit" && comment.action != "merge" && comment.action != "refer"
&& (isManageable || context.loginAccount.map(_.userName == comment.commentedUserName).getOrElse(false))){
&& (isManageable || context.loginAccount.exists(_.userName == comment.commentedUserName))){
<span class="pull-right">
<a href="#" data-comment-id="@comment.commentId"><i class="octicon octicon-pencil" aria-label="Edit"></i></a>&nbsp;
<a href="#" data-comment-id="@comment.commentId"><i class="octicon octicon-x" aria-label="Remove"></i></a>
@@ -32,12 +32,14 @@
}
</div>
<div class="panel-body markdown-body" id="commentContent-@comment.commentId">
@helpers.markdown(
markdown = comment.content,
repository = repository,
@helpers.renderMarkup(
filePath = List("temporary.md"),
fileContent = comment.content,
branch = repository.repository.defaultBranch,
repository = repository,
enableWikiLink = false,
enableRefsLink = true,
enableAnchor = true,
enableLineBreaks = true,
enableTaskList = true,
hasWritePermission = isManageable
@@ -52,18 +54,20 @@
@helpers.user(issue.get.openedUserName, styleClass="username strong")
<span class="muted">commented @gitbucket.core.helper.html.datetimeago(issue.get.registeredDate)</span>
<span class="pull-right">
@if(isManageable || context.loginAccount.map(_.userName == issue.get.openedUserName).getOrElse(false)){
@if(isManageable || context.loginAccount.exists(_.userName == issue.get.openedUserName)){
<a href="#" data-issue-id="@issue.get.issueId"><i class="octicon octicon-pencil" aria-label="Edit"></i></a>
}
</span>
</div>
<div class="panel-body markdown-body" id="issueContent">
@helpers.markdown(
markdown = issue.get.content getOrElse "No description provided.",
repository = repository,
@helpers.renderMarkup(
filePath = List("temporary.md"),
fileContent = issue.get.content getOrElse "No description provided.",
branch = repository.repository.defaultBranch,
repository = repository,
enableWikiLink = false,
enableRefsLink = true,
enableAnchor = true,
enableLineBreaks = true,
enableTaskList = true,
hasWritePermission = isManageable
@@ -331,12 +335,14 @@
}
</div>
<div class="panel-body markdown-body commit-commentContent-@comment.commentId">
@helpers.markdown(
markdown = comment.content,
repository = repository,
@helpers.renderMarkup(
filePath = List("temporary.md"),
fileContent = comment.content,
branch = repository.repository.defaultBranch,
repository = repository,
enableWikiLink = false,
enableRefsLink = true,
enableAnchor = true,
enableLineBreaks = true,
enableTaskList = true,
hasWritePermission = isManageable
@@ -400,7 +406,7 @@ $(function(){
// diff view
const tr = comment.closest('.not-diff');
if(tr.length > 0){
if(tr.prev('.not-diff').length == 0){
if(tr.prev('.not-diff').length === 0){
tr.next('.not-diff:has(.reply-comment)').remove();
}
tr.remove();
@@ -410,7 +416,7 @@ $(function(){
const panel = comment.closest('div.panel:has(.commit-comment-box)');
if(panel.length > 0){
comment.parent('.commit-comment-box').remove();
if(panel.has('.commit-comment-box').length == 0){
if(panel.has('.commit-comment-box').length === 0){
panel.remove();
}
} else {

View File

@@ -16,7 +16,7 @@
@gitbucket.core.html.menu("issues", repository){
<div>
<div class="show-title pull-right">
@if(isManageable || context.loginAccount.map(_.userName == issue.openedUserName).getOrElse(false)){
@if(isManageable || context.loginAccount.exists(_.userName == issue.openedUserName)){
<a class="btn btn-default" href="#" id="edit">Edit</a>
}
@if(isEditable){

View File

@@ -131,7 +131,7 @@
@collaborators.map { collaborator =>
<li>
<a href="javascript:void(0);" class="toggle-assign" data-name="@collaborator">
@gitbucket.core.helper.html.checkicon(issueAssignees.exists(_.assigneeUserName == collaborator))@helpers.avatar(collaborator, 20) @collaborator
@gitbucket.core.helper.html.checkicon(issueAssignees.exists(_.assigneeUserName == collaborator))@helpers.avatar(collaborator, 20) @helpers.displayUserName(collaborator)
</a>
</li>
}

View File

@@ -83,7 +83,18 @@
@defining(helpers.isRenderable(pathList.last)){ isRenderable =>
@if(!isBlame && isRenderable) {
<div class="box-content-bottom @if(content.viewType == "text"){ markdown-body } " style="padding-left: 20px; padding-right: 20px;">
@helpers.renderMarkup(pathList, content.content.getOrElse(""), branch, repository, false, false, true)
@helpers.renderMarkup(
filePath = pathList,
fileContent = content.content.getOrElse(""),
branch = branch,
repository = repository,
enableWikiLink = false,
enableRefsLink = false,
enableAnchor = true,
enableLineBreaks = false,
enableTaskList = true,
hasWritePermission = hasWritePermission
)
</div>
}else{
@if(content.viewType == "text"){

View File

@@ -208,29 +208,31 @@ $(function(){
$('#preview').show();
$('#btn-code').removeClass('active');
@if(fileName.map(helpers.isRenderable _).getOrElse(false)) {
// update preview
$('#preview').html('<img src="@helpers.assets("/common/images/indicator.gif")"> Previewing...');
$.post('@helpers.url(repository)/_preview', {
content : editor.getValue(),
enableWikiLink : false,
filename : $('#newFileName').val(),
enableRefsLink : false,
enableLineBreaks : false,
enableTaskList : false
}, function(data){
$('#preview').empty().append(
$('<div class="markdown-body" style="padding-left: 20px; padding-right: 20px;">').html(data));
prettyPrint();
});
} else {
// Show diff
$('#preview').empty()
.append($('<div id="diffText">'))
.append($('<textarea id="newText" style="display: none;">').data('file-name',$("#newFileName").val()).data('val', editor.getValue()))
.append($('<textarea id="oldText" style="display: none;">').data('file-name',$("#oldFileName").val()).data('val', $('#initial').val()));
diffUsingJS('oldText', 'newText', 'diffText', 1);
}
$.get("@context.baseUrl/_is_renderable?filename=" + encodeURIComponent($('#newFileName').val()), function(data) {
if (data === 'true') {
// update preview
$('#preview').html('<img src="@helpers.assets("/common/images/indicator.gif")"> Previewing...');
$.post('@helpers.url(repository)/_preview', {
content : editor.getValue(),
enableWikiLink : false,
filename : $('#path').val() + '/' + $('#newFileName').val(),
enableRefsLink : false,
enableLineBreaks : false,
enableTaskList : false
}, function(data){
$('#preview').empty().append(
$('<div class="markdown-body" style="padding-left: 20px; padding-right: 20px;">').html(data));
prettyPrint();
});
} else {
// Show diff
$('#preview').empty()
.append($('<div id="diffText">'))
.append($('<textarea id="newText" style="display: none;">').data('file-name',$("#newFileName").val()).data('val', editor.getValue()))
.append($('<textarea id="oldText" style="display: none;">').data('file-name',$("#oldFileName").val()).data('val', $('#initial').val()));
diffUsingJS('oldText', 'newText', 'diffText', 1);
}
});
});
});
</script>

View File

@@ -220,7 +220,7 @@
}
</div>
</div>
<div class="box-content-bottom markdown-body" style="padding-left: 20px; padding-right: 20px;">@helpers.renderMarkup(filePath, content, branch, repository, false, false, true)</div>
<div class="box-content-bottom markdown-body" style="padding-left: 20px; padding-right: 20px;">@helpers.renderMarkup(filePath, content, branch, repository, false, false, false, true, true)</div>
}
}
}

View File

@@ -48,25 +48,25 @@ $(function(){
});
$('.add').click(function(){
var id = $(this).attr('id') == 'addCollaborator' ? 'collaborator' : 'group';
const id = $(this).attr('id') === 'addCollaborator' ? 'collaborator' : 'group';
$('#error-' + id).text('');
var userName = $('#userName-' + id).val();
const userName = $('#userName-' + id).val();
// check empty
if($.trim(userName) == ''){
if($.trim(userName) === ''){
return false;
}
// check owner
var owner = '@repository.owner' == userName
const owner = '@repository.owner' === userName
if(owner){
$('#error-' + id).text('User is owner of this repository.');
return false;
}
// check duplication
var exists = $('#' + id + '-list li').filter(function(){
return $(this).data('name') == userName;
const exists = $('#' + id + '-list li').filter(function(){
return $(this).data('name') === userName;
}).length > 0;
if(exists){
$('#error-' + id).text('User has been already added.');
@@ -76,7 +76,7 @@ $(function(){
// check existence
$.post('@context.path/_user/existence', { 'userName': userName },
function(data, status){
if(data != ''){
if(data !== ''){
addListHTML(userName, '@Role.ADMIN.name', '#' + id + '-list');
$('#userName-' + id).val('');
} else {
@@ -91,7 +91,7 @@ $(function(){
// Don't submit form by ENTER key
$('#userName-collaborator, #userName-group').keypress(function(e){
return !(e.keyCode == 13);
return !(e.keyCode === 13);
});
@collaborators.map { case (collaborator, isGroup) =>
@@ -99,16 +99,16 @@ $(function(){
}
function addListHTML(userName, role, id){
var adminButton = $('<label class="btn btn-default btn-mini"><input type="radio" value="@Role.ADMIN.name" name="' + userName + '">Admin</label>');
if(role == '@Role.ADMIN.name'){
const adminButton = $('<label class="btn btn-default btn-mini"><input type="radio" value="@Role.ADMIN.name" name="' + userName + '">Admin</label>');
if(role === '@Role.ADMIN.name'){
adminButton.addClass('active');
}
var writeButton = $('<label class="btn btn-default btn-mini"><input type="radio" value="@Role.DEVELOPER.name" name="' + userName + '">Developer</label>');
if(role == '@Role.DEVELOPER.name'){
const writeButton = $('<label class="btn btn-default btn-mini"><input type="radio" value="@Role.DEVELOPER.name" name="' + userName + '">Developer</label>');
if(role === '@Role.DEVELOPER.name'){
writeButton.addClass('active');
}
var readButton = $('<label class="btn btn-default btn-mini"><input type="radio" value="@Role.GUEST.name" name="' + userName + '">Guest</label>');
if(role == '@Role.GUEST.name'){
const readButton = $('<label class="btn btn-default btn-mini"><input type="radio" value="@Role.GUEST.name" name="' + userName + '">Guest</label>');
if(role === '@Role.GUEST.name'){
readButton.addClass('active');
}
@@ -124,13 +124,13 @@ $(function(){
}
function updateValues(){
var collaborators = $('#collaborator-list li').map(function(i, e){
var userName = $(e).data('name');
const collaborators = $('#collaborator-list li').map(function(i, e){
const userName = $(e).data('name');
return userName + ':' + $(e).find('label.active input[type=radio]').attr('value');
}).get().join(',');
var groups = $('#group-list li').map(function(i, e){
var userName = $(e).data('name');
const groups = $('#group-list li').map(function(i, e){
const userName = $(e).data('name');
return userName + ':' + $(e).find('label.active input[type=radio]').attr('value');
}).get().join(',');

View File

@@ -59,7 +59,17 @@
@if(isEditable){
<a href="@helpers.url(repository)/wiki/_Sidebar/_edit" style="text-decoration: none;"><span class="octicon octicon-pencil pull-right"></span></a>
}
@helpers.markdown(sidebarPage.content, repository, "master", true, false, false, false, pages)
@helpers.renderMarkup(
filePath = sidebarPage.name.split("/").toList,
fileContent = sidebarPage.content,
branch = "master",
repository = repository,
enableWikiLink = true,
enableRefsLink = false,
enableAnchor = false,
enableLineBreaks = false,
enableTaskList = false
)
</div>
}.getOrElse{
@if(isEditable){
@@ -82,16 +92,16 @@
</div>
<div class="wiki-main">
<div class="markdown-body">
@helpers.markdown(
markdown = page.content,
repository = repository,
@helpers.renderMarkup(
filePath = page.name.split("/").toList,
fileContent = page.content,
branch = "master",
repository = repository,
enableWikiLink = true,
enableRefsLink = false,
enableAnchor = true,
enableLineBreaks = false,
enableTaskList = false,
hasWritePermission = false,
pages = pages
enableTaskList = false
)
</div>
@footer.map { footerPage =>
@@ -99,15 +109,16 @@
@if(isEditable){
<a href="@helpers.url(repository)/wiki/_Footer/_edit" style="text-decoration: none;"><span class="octicon octicon-pencil pull-right"></span></a>
}
@helpers.markdown(
markdown = footerPage.content,
repository = repository,
branch = "master",
enableWikiLink = true,
enableRefsLink = false,
@helpers.renderMarkup(
filePath = footerPage.name.split("/").toList,
fileContent = footerPage.content,
branch = "master",
repository = repository,
enableWikiLink = true,
enableRefsLink = false,
enableAnchor = false,
enableLineBreaks = false,
enableAnchor = false,
pages = pages
enableTaskList = false
)
</div>
}.getOrElse{

View File

@@ -1536,7 +1536,6 @@ div.markdown-body table th,
div.markdown-body table td {
padding: 8px;
line-height: 20px;
text-align: left;
vertical-align: top;
border-top: 1px solid #dddddd;
}

View File

@@ -1,14 +1,14 @@
package gitbucket.core
import java.sql.DriverManager
import com.dimafeng.testcontainers.{MySQLContainer, PostgreSQLContainer}
import io.github.gitbucket.solidbase.Solidbase
import io.github.gitbucket.solidbase.model.Module
import liquibase.database.core.{H2Database, MySQLDatabase, PostgresDatabase}
import org.junit.runner.Description
import org.scalatest.funsuite.AnyFunSuite
import org.scalatest.Tag
import org.testcontainers.postgresql.PostgreSQLContainer
import org.testcontainers.mysql.MySQLContainer
import org.testcontainers.utility.DockerImageName
object ExternalDBTest extends Tag("ExternalDBTest")
@@ -26,24 +26,19 @@ class GitBucketCoreModuleSpec extends AnyFunSuite {
implicit private val suiteDescription: Description = Description.createSuiteDescription(getClass)
Seq("8.0", "5.7").foreach { tag =>
Seq("8.4", "5.7").foreach { tag =>
test(s"Migration MySQL $tag", ExternalDBTest) {
val container = new MySQLContainer() {
override val container: org.testcontainers.containers.MySQLContainer[?] =
new org.testcontainers.containers.MySQLContainer(s"mysql:$tag") {
override def getDriverClassName = "org.mariadb.jdbc.Driver"
override def getJdbcUrl: String = super.getJdbcUrl + "?permitMysqlScheme"
}
// TODO https://jira.mariadb.org/browse/CONJ-663
container.withCommand("mysqld --default-authentication-plugin=mysql_native_password")
val container = new MySQLContainer(s"mysql:$tag") {
override def getDriverClassName = "org.mariadb.jdbc.Driver"
override def getJdbcUrl: String = super.getJdbcUrl + "?permitMysqlScheme"
}
container.start()
try {
new Solidbase().migrate(
DriverManager.getConnection(
container.jdbcUrl,
container.username,
container.password
container.getJdbcUrl,
container.getUsername,
container.getPassword
),
Thread.currentThread().getContextClassLoader(),
new MySQLDatabase(),
@@ -57,12 +52,12 @@ class GitBucketCoreModuleSpec extends AnyFunSuite {
Seq("11", "10").foreach { tag =>
test(s"Migration PostgreSQL $tag", ExternalDBTest) {
val container = PostgreSQLContainer(DockerImageName.parse(s"postgres:$tag"))
val container = new PostgreSQLContainer(DockerImageName.parse(s"postgres:$tag"))
container.start()
try {
new Solidbase().migrate(
DriverManager.getConnection(container.jdbcUrl, container.username, container.password),
DriverManager.getConnection(container.getJdbcUrl, container.getUsername, container.getPassword),
Thread.currentThread().getContextClassLoader(),
new PostgresDatabase(),
new Module(GitBucketCoreModule.getModuleId, GitBucketCoreModule.getVersions)

View File

@@ -85,7 +85,8 @@ trait ServiceSpecBase {
maxDiffFiles = 100,
maxDiffLines = 1000
),
defaultBranch = "main"
defaultBranch = "main",
showFullName = false
)
def withTestDB[A](action: (Session) => A): A = {

View File

@@ -107,7 +107,7 @@ class AvatarImageProviderSpec extends AnyFunSpec {
provider.toHtml("user", 20, "hoge@hoge.com", true).toString ==
"""<img src="/_unknown/_avatar" class="avatar-mini" style="width: 20px; height: 20px;"
| alt="@user"
| data-toggle="tooltip" title="user" />""".stripMargin
| data-toggle="tooltip" title="@user" />""".stripMargin
)
}
@@ -125,7 +125,7 @@ class AvatarImageProviderSpec extends AnyFunSpec {
provider.toHtml("""<user>"<name>""", 20, "hoge@hoge.com", true).toString ==
"""<img src="/_unknown/_avatar" class="avatar-mini" style="width: 20px; height: 20px;"
| alt="@&lt;user&gt;&quot;&lt;name&gt;"
| data-toggle="tooltip" title="&lt;user&gt;&quot;&lt;name&gt;" />""".stripMargin
| data-toggle="tooltip" title="@&lt;user&gt;&quot;&lt;name&gt;" />""".stripMargin
)
}
}
@@ -196,7 +196,8 @@ class AvatarImageProviderSpec extends AnyFunSpec {
maxDiffFiles = 100,
maxDiffLines = 1000
),
"main"
defaultBranch = "main",
showFullName = false
)
/**