mirror of
https://github.com/gitbucket/gitbucket.git
synced 2025-10-30 01:56:09 +01:00
Compare commits
44 Commits
revert_pul
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6fe903afab | ||
|
|
0d94865633 | ||
|
|
2fbdead64b | ||
|
|
72a354931e | ||
|
|
cb8affcd0d | ||
|
|
a5a997eb40 | ||
|
|
b4220aab68 | ||
|
|
c8a4798f86 | ||
|
|
6b8e9e8892 | ||
|
|
9ab9363d0b | ||
|
|
1edb18a147 | ||
|
|
d8b4bf3033 | ||
|
|
6597c4490b | ||
|
|
daaf1696ad | ||
|
|
53a1ca7874 | ||
|
|
f045fa4c6a | ||
|
|
53a7c5adf8 | ||
|
|
b142eca9a5 | ||
|
|
ba753a373b | ||
|
|
95ceca75a4 | ||
|
|
f16cc117a9 | ||
|
|
af66f8f746 | ||
|
|
d9cc57e8e0 | ||
|
|
72b6dad3a2 | ||
|
|
18f396b4a2 | ||
|
|
9f3fde8de2 | ||
|
|
dfd6f80b63 | ||
|
|
119d91210c | ||
|
|
75ef30ee03 | ||
|
|
d1cf9dd600 | ||
|
|
9c9fea908c | ||
|
|
1145c4d0f6 | ||
|
|
d847fc6e0f | ||
|
|
bb9585f7a6 | ||
|
|
e91411fa45 | ||
|
|
adbc065a6f | ||
|
|
862283b729 | ||
|
|
217df7012c | ||
|
|
e672d41e77 | ||
|
|
d975700bd4 | ||
|
|
f65e41561a | ||
|
|
dab4f33ed9 | ||
|
|
5ff45ef5ae | ||
|
|
af7c622647 |
8
.github/workflows/build.yml
vendored
8
.github/workflows/build.yml
vendored
@@ -9,9 +9,9 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
java: [17, 21]
|
||||
java: [17, 25]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
- name: Cache
|
||||
uses: actions/cache@v4
|
||||
env:
|
||||
@@ -23,7 +23,7 @@ jobs:
|
||||
~/.cache/coursier/v1
|
||||
key: build-${{ env.cache-name }}-${{ hashFiles('build.sbt') }}
|
||||
- name: Set up JDK
|
||||
uses: actions/setup-java@v4
|
||||
uses: actions/setup-java@v5
|
||||
with:
|
||||
java-version: ${{ matrix.java }}
|
||||
distribution: adopt
|
||||
@@ -36,7 +36,7 @@ jobs:
|
||||
- name: Build executable
|
||||
run: sbt executable
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v5
|
||||
with:
|
||||
name: gitbucket-java${{ matrix.java }}-${{ github.sha }}
|
||||
path: ./target/executable/gitbucket.*
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
version = "3.9.9"
|
||||
version = "3.10.1"
|
||||
project.git = true
|
||||
|
||||
maxColumn = 120
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
# Changelog
|
||||
All changes to the project will be documented in this file.
|
||||
|
||||
## 4.44.0 - 23 Sep 2025
|
||||
- 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
|
||||
- Upgrade H2 database from 1.x to 2.x
|
||||
|
||||
|
||||
11
README.md
11
README.md
@@ -59,12 +59,15 @@ 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.43.x
|
||||
What's New in 4.44.x
|
||||
-------------
|
||||
## 4.43.0 - 29 Jun 2025
|
||||
- Upgrade H2 database from 1.x to 2.x
|
||||
## 4.44.0 - 23 Sep 2025
|
||||
- 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
|
||||
|
||||
Note that upgrading from h2 1.x to 2.x requires data file migration: https://www.h2database.com/html/migration-to-v2.html
|
||||
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
|
||||
|
||||
It can't be done automatically using GitBucket's auto migration mechanism because it relies on database itself. So, users who use h2 will have to dump and recreate their database manually with the following steps:
|
||||
```bash
|
||||
|
||||
65
build.sbt
65
build.sbt
@@ -2,9 +2,9 @@ import com.jsuereth.sbtpgp.PgpKeys._
|
||||
|
||||
val Organization = "io.github.gitbucket"
|
||||
val Name = "gitbucket"
|
||||
val GitBucketVersion = "4.43.0"
|
||||
val GitBucketVersion = "4.44.0"
|
||||
val ScalatraVersion = "3.1.2"
|
||||
val JettyVersion = "10.0.25"
|
||||
val JettyVersion = "10.0.26"
|
||||
val JgitVersion = "6.10.1.202505221210-r"
|
||||
|
||||
lazy val root = (project in file("."))
|
||||
@@ -14,9 +14,9 @@ sourcesInBase := false
|
||||
organization := Organization
|
||||
name := Name
|
||||
version := GitBucketVersion
|
||||
scalaVersion := "2.13.16"
|
||||
scalaVersion := "2.13.17"
|
||||
|
||||
crossScalaVersions += "3.7.2"
|
||||
crossScalaVersions += "3.7.3"
|
||||
|
||||
// scalafmtOnCompile := true
|
||||
|
||||
@@ -37,38 +37,37 @@ libraryDependencies ++= Seq(
|
||||
"org.apache.commons" % "commons-email" % "1.6.0",
|
||||
"commons-net" % "commons-net" % "3.12.0",
|
||||
"org.apache.httpcomponents" % "httpclient" % "4.5.14",
|
||||
"org.apache.sshd" % "apache-sshd" % "2.15.0" exclude ("org.slf4j", "slf4j-jdk14") exclude (
|
||||
"org.apache.sshd" % "apache-sshd" % "2.16.0" exclude ("org.slf4j", "slf4j-jdk14") exclude (
|
||||
"org.apache.sshd",
|
||||
"sshd-mina"
|
||||
) exclude ("org.apache.sshd", "sshd-netty")
|
||||
exclude ("org.apache.sshd", "sshd-spring-sftp"),
|
||||
"org.apache.tika" % "tika-core" % "3.2.2",
|
||||
"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.7",
|
||||
"ch.qos.logback" % "logback-classic" % "1.5.18",
|
||||
"com.zaxxer" % "HikariCP" % "7.0.1" exclude ("org.slf4j", "slf4j-api"),
|
||||
"com.typesafe" % "config" % "1.4.4",
|
||||
"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.27",
|
||||
"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.18.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.329" % "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.12",
|
||||
"org.postgresql" % "postgresql" % "42.7.8",
|
||||
"ch.qos.logback" % "logback-classic" % "1.5.20",
|
||||
"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.30",
|
||||
"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",
|
||||
"org.testcontainers" % "testcontainers-mysql" % "2.0.1" % "test",
|
||||
"org.testcontainers" % "testcontainers-postgresql" % "2.0.1" % "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"
|
||||
)
|
||||
|
||||
// Compiler settings
|
||||
@@ -192,7 +191,7 @@ executableKey := {
|
||||
|
||||
// zip it up
|
||||
IO delete (temp / "META-INF" / "MANIFEST.MF")
|
||||
val contentMappings = (temp.allPaths --- PathFinder(temp)).get pair { file =>
|
||||
val contentMappings = (temp.allPaths --- PathFinder(temp)).get() pair { file =>
|
||||
IO.relativizeFile(temp, file)
|
||||
}
|
||||
val manifest = new JarManifest
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1 +1 @@
|
||||
sbt.version=1.11.4
|
||||
sbt.version=1.11.7
|
||||
|
||||
@@ -5,7 +5,7 @@ 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.7.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.0")
|
||||
|
||||
addDependencyTreePlugin
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -1076,14 +1076,9 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
||||
redirect(s"${repository.owner}/${repository.name}/releases")
|
||||
})
|
||||
|
||||
get("/:owner/:repository/archive/:name")(referrersOnly { repository =>
|
||||
val name = params("name")
|
||||
archiveRepository(name, repository, "")
|
||||
})
|
||||
|
||||
get("/:owner/:repository/archive/*/:name")(referrersOnly { repository =>
|
||||
val name = params("name")
|
||||
val path = multiParams("splat").head
|
||||
get("/:owner/:repository/archive/*")(referrersOnly { repository =>
|
||||
val name = multiParams("splat").mkString("/")
|
||||
val path = params.get("path").getOrElse("")
|
||||
archiveRepository(name, repository, path)
|
||||
})
|
||||
|
||||
|
||||
@@ -306,7 +306,8 @@ trait WikiControllerBase extends ControllerBase {
|
||||
|
||||
get("/:owner/:repository/wiki/_history")(referrersOnly { repository =>
|
||||
Using.resource(Git.open(getWikiRepositoryDir(repository.owner, repository.name))) { git =>
|
||||
JGitUtil.getCommitLog(git, "master") match {
|
||||
val branch = getWikiBranch(repository.owner, repository.name)
|
||||
JGitUtil.getCommitLog(git, branch) match {
|
||||
case Right((logs, hasNext)) => html.history(None, logs, repository, isEditable(repository))
|
||||
case Left(_) => NotFound()
|
||||
}
|
||||
@@ -316,7 +317,8 @@ trait WikiControllerBase extends ControllerBase {
|
||||
get("/:owner/:repository/wiki/_blob/*")(referrersOnly { repository =>
|
||||
val path = multiParams("splat").head
|
||||
Using.resource(Git.open(getWikiRepositoryDir(repository.owner, repository.name))) { git =>
|
||||
val revCommit = JGitUtil.getRevCommitFromId(git, git.getRepository.resolve("master"))
|
||||
val branch = getWikiBranch(repository.owner, repository.name)
|
||||
val revCommit = JGitUtil.getRevCommitFromId(git, git.getRepository.resolve(branch))
|
||||
|
||||
getPathObjectId(git, path, revCommit).map { objectId =>
|
||||
responseRawFile(git, objectId, path, repository)
|
||||
|
||||
@@ -142,10 +142,11 @@ trait ApiRepositoryContentsControllerBase extends ControllerBase {
|
||||
val revCommit = JGitUtil.getRevCommitFromId(git, git.getRepository.resolve(branch))
|
||||
revCommit.name
|
||||
}
|
||||
val paths = multiParams("splat").head.split("/")
|
||||
val fullPath = multiParams("splat").head
|
||||
val paths = fullPath.split("/")
|
||||
val path = paths.take(paths.size - 1).toList.mkString("/")
|
||||
Using.resource(Git.open(getRepositoryDir(params("owner"), params("repository")))) { git =>
|
||||
val fileInfo = getFileInfo(git, commit, path, false)
|
||||
val fileInfo = getFileInfo(git, commit, fullPath, ignoreCase = false)
|
||||
|
||||
fileInfo match {
|
||||
case Some(f) if !data.sha.contains(f.id.getName) =>
|
||||
|
||||
@@ -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) ||
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
<div class="head" style="height: 24px;">
|
||||
<div class="pull-right">
|
||||
<div class="btn-group">
|
||||
<a href="@{helpers.url(repository)}/archive@if(pathList.length > 0){/@pathList.map(helpers.urlEncode).mkString("/")}/@{helpers.urlEncode(branch)}.zip" class="btn btn-sm btn-default pc"><i class="octicon octicon-cloud-download"></i> Download ZIP</a>
|
||||
<a href="@{helpers.url(repository)}/archive/@{helpers.urlEncode(branch)}.zip@if(pathList.nonEmpty){?path=@helpers.urlEncode(pathList.mkString("/"))}" class="btn btn-sm btn-default pc"><i class="octicon octicon-cloud-download"></i> Download ZIP</a>
|
||||
<a href="@helpers.url(repository)/find/@helpers.encodeRefName(branch)" class="btn btn-sm btn-default" data-hotkey="t" title="Search files"><i class="octicon octicon-search" aria-label="Search files"></i></a>
|
||||
<a href="@helpers.url(repository)/commits/@helpers.encodeRefName((branch :: pathList).mkString("/"))" class="btn btn-sm btn-default"><i class="octicon octicon-history"></i> @if(commitCount > 10000){10000+} else {@commitCount} @helpers.plural(commitCount, "commit")</a>
|
||||
</div>
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -183,14 +183,14 @@ class ApiIntegrationTest extends AnyFunSuite {
|
||||
.branch("main")
|
||||
.content("create")
|
||||
.message("Create content")
|
||||
.path("README.md")
|
||||
.path("test.txt")
|
||||
.commit()
|
||||
|
||||
assert(createResult.getContent.isFile == true)
|
||||
assert(createResult.getContent.isFile)
|
||||
assert(IOUtils.toString(createResult.getContent.read(), "UTF-8") == "create")
|
||||
|
||||
val content1 = repo.getFileContent("README.md")
|
||||
assert(content1.isFile == true)
|
||||
val content1 = repo.getFileContent("test.txt")
|
||||
assert(content1.isFile)
|
||||
assert(IOUtils.toString(content1.read(), "UTF-8") == "create")
|
||||
assert(content1.getSha == createResult.getContent.getSha)
|
||||
|
||||
@@ -200,14 +200,14 @@ class ApiIntegrationTest extends AnyFunSuite {
|
||||
.branch("main")
|
||||
.content("update")
|
||||
.message("Update content")
|
||||
.path("README.md")
|
||||
.path("test.txt")
|
||||
.sha(content1.getSha)
|
||||
.commit()
|
||||
|
||||
assert(updateResult.getContent.isFile == true)
|
||||
assert(updateResult.getContent.isFile)
|
||||
assert(IOUtils.toString(updateResult.getContent.read(), "UTF-8") == "update")
|
||||
|
||||
val content2 = repo.getFileContent("README.md")
|
||||
val content2 = repo.getFileContent("test.txt")
|
||||
assert(content2.isFile == true)
|
||||
assert(IOUtils.toString(content2.read(), "UTF-8") == "update")
|
||||
assert(content2.getSha == updateResult.getContent.getSha)
|
||||
|
||||
Reference in New Issue
Block a user