mirror of
https://github.com/go-gitea/gitea
synced 2025-07-03 09:07:19 +00:00
Move admin routers from /admin to /-/admin (#32189)
Resolve #32181 --------- Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
This commit is contained in:
@ -45,7 +45,7 @@ func ListHooks(ctx *context.APIContext) {
|
||||
}
|
||||
hooks := make([]*api.Hook, len(sysHooks))
|
||||
for i, hook := range sysHooks {
|
||||
h, err := webhook_service.ToHook(setting.AppURL+"/admin", hook)
|
||||
h, err := webhook_service.ToHook(setting.AppURL+"/-/admin", hook)
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "convert.ToHook", err)
|
||||
return
|
||||
@ -83,7 +83,7 @@ func GetHook(ctx *context.APIContext) {
|
||||
}
|
||||
return
|
||||
}
|
||||
h, err := webhook_service.ToHook("/admin/", hook)
|
||||
h, err := webhook_service.ToHook("/-/admin/", hook)
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "convert.ToHook", err)
|
||||
return
|
||||
|
@ -100,7 +100,7 @@ func checkCreateHookOption(ctx *context.APIContext, form *api.CreateHookOption)
|
||||
func AddSystemHook(ctx *context.APIContext, form *api.CreateHookOption) {
|
||||
hook, ok := addHook(ctx, form, 0, 0)
|
||||
if ok {
|
||||
h, err := webhook_service.ToHook(setting.AppSubURL+"/admin", hook)
|
||||
h, err := webhook_service.ToHook(setting.AppSubURL+"/-/admin", hook)
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "convert.ToHook", err)
|
||||
return
|
||||
@ -268,7 +268,7 @@ func EditSystemHook(ctx *context.APIContext, form *api.EditHookOption, hookID in
|
||||
ctx.Error(http.StatusInternalServerError, "GetSystemOrDefaultWebhook", err)
|
||||
return
|
||||
}
|
||||
h, err := webhook_service.ToHook(setting.AppURL+"/admin", updated)
|
||||
h, err := webhook_service.ToHook(setting.AppURL+"/-/admin", updated)
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "convert.ToHook", err)
|
||||
return
|
||||
|
@ -185,9 +185,9 @@ func DashboardPost(ctx *context.Context) {
|
||||
}
|
||||
}
|
||||
if form.From == "monitor" {
|
||||
ctx.Redirect(setting.AppSubURL + "/admin/monitor/cron")
|
||||
ctx.Redirect(setting.AppSubURL + "/-/admin/monitor/cron")
|
||||
} else {
|
||||
ctx.Redirect(setting.AppSubURL + "/admin")
|
||||
ctx.Redirect(setting.AppSubURL + "/-/admin")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -23,8 +23,8 @@ var (
|
||||
func newOAuth2CommonHandlers() *user_setting.OAuth2CommonHandlers {
|
||||
return &user_setting.OAuth2CommonHandlers{
|
||||
OwnerID: 0,
|
||||
BasePathList: fmt.Sprintf("%s/admin/applications", setting.AppSubURL),
|
||||
BasePathEditPrefix: fmt.Sprintf("%s/admin/applications/oauth2", setting.AppSubURL),
|
||||
BasePathList: fmt.Sprintf("%s/-/admin/applications", setting.AppSubURL),
|
||||
BasePathEditPrefix: fmt.Sprintf("%s/-/admin/applications/oauth2", setting.AppSubURL),
|
||||
TplAppEdit: tplSettingsOauth2ApplicationEdit,
|
||||
}
|
||||
}
|
||||
|
@ -324,7 +324,7 @@ func NewAuthSourcePost(ctx *context.Context) {
|
||||
log.Trace("Authentication created by admin(%s): %s", ctx.Doer.Name, form.Name)
|
||||
|
||||
ctx.Flash.Success(ctx.Tr("admin.auths.new_success", form.Name))
|
||||
ctx.Redirect(setting.AppSubURL + "/admin/auths")
|
||||
ctx.Redirect(setting.AppSubURL + "/-/admin/auths")
|
||||
}
|
||||
|
||||
// EditAuthSource render editing auth source page
|
||||
@ -437,7 +437,7 @@ func EditAuthSourcePost(ctx *context.Context) {
|
||||
log.Trace("Authentication changed by admin(%s): %d", ctx.Doer.Name, source.ID)
|
||||
|
||||
ctx.Flash.Success(ctx.Tr("admin.auths.update_success"))
|
||||
ctx.Redirect(setting.AppSubURL + "/admin/auths/" + strconv.FormatInt(form.ID, 10))
|
||||
ctx.Redirect(setting.AppSubURL + "/-/admin/auths/" + strconv.FormatInt(form.ID, 10))
|
||||
}
|
||||
|
||||
// DeleteAuthSource response for deleting an auth source
|
||||
@ -454,11 +454,11 @@ func DeleteAuthSource(ctx *context.Context) {
|
||||
} else {
|
||||
ctx.Flash.Error(fmt.Sprintf("auth_service.DeleteSource: %v", err))
|
||||
}
|
||||
ctx.JSONRedirect(setting.AppSubURL + "/admin/auths/" + url.PathEscape(ctx.PathParam(":authid")))
|
||||
ctx.JSONRedirect(setting.AppSubURL + "/-/admin/auths/" + url.PathEscape(ctx.PathParam(":authid")))
|
||||
return
|
||||
}
|
||||
log.Trace("Authentication deleted by admin(%s): %d", ctx.Doer.Name, source.ID)
|
||||
|
||||
ctx.Flash.Success(ctx.Tr("admin.auths.deletion_success"))
|
||||
ctx.JSONRedirect(setting.AppSubURL + "/admin/auths")
|
||||
ctx.JSONRedirect(setting.AppSubURL + "/-/admin/auths")
|
||||
}
|
||||
|
@ -40,7 +40,7 @@ func SendTestMail(ctx *context.Context) {
|
||||
ctx.Flash.Info(ctx.Tr("admin.config.test_mail_sent", email))
|
||||
}
|
||||
|
||||
ctx.Redirect(setting.AppSubURL + "/admin/config")
|
||||
ctx.Redirect(setting.AppSubURL + "/-/admin/config")
|
||||
}
|
||||
|
||||
// TestCache test the cache settings
|
||||
@ -56,7 +56,7 @@ func TestCache(ctx *context.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
ctx.Redirect(setting.AppSubURL + "/admin/config")
|
||||
ctx.Redirect(setting.AppSubURL + "/-/admin/config")
|
||||
}
|
||||
|
||||
func shadowPasswordKV(cfgItem, splitter string) string {
|
||||
|
@ -134,7 +134,7 @@ func ActivateEmail(ctx *context.Context) {
|
||||
ctx.Flash.Info(ctx.Tr("admin.emails.updated"))
|
||||
}
|
||||
|
||||
redirect, _ := url.Parse(setting.AppSubURL + "/admin/emails")
|
||||
redirect, _ := url.Parse(setting.AppSubURL + "/-/admin/emails")
|
||||
q := url.Values{}
|
||||
if val := ctx.FormTrim("q"); len(val) > 0 {
|
||||
q.Set("q", val)
|
||||
|
@ -36,8 +36,8 @@ func DefaultOrSystemWebhooks(ctx *context.Context) {
|
||||
sys["Title"] = ctx.Tr("admin.systemhooks")
|
||||
sys["Description"] = ctx.Tr("admin.systemhooks.desc", "https://docs.gitea.com/usage/webhooks")
|
||||
sys["Webhooks"], err = webhook.GetSystemWebhooks(ctx, optional.None[bool]())
|
||||
sys["BaseLink"] = setting.AppSubURL + "/admin/hooks"
|
||||
sys["BaseLinkNew"] = setting.AppSubURL + "/admin/system-hooks"
|
||||
sys["BaseLink"] = setting.AppSubURL + "/-/admin/hooks"
|
||||
sys["BaseLinkNew"] = setting.AppSubURL + "/-/admin/system-hooks"
|
||||
if err != nil {
|
||||
ctx.ServerError("GetWebhooksAdmin", err)
|
||||
return
|
||||
@ -46,8 +46,8 @@ func DefaultOrSystemWebhooks(ctx *context.Context) {
|
||||
def["Title"] = ctx.Tr("admin.defaulthooks")
|
||||
def["Description"] = ctx.Tr("admin.defaulthooks.desc", "https://docs.gitea.com/usage/webhooks")
|
||||
def["Webhooks"], err = webhook.GetDefaultWebhooks(ctx)
|
||||
def["BaseLink"] = setting.AppSubURL + "/admin/hooks"
|
||||
def["BaseLinkNew"] = setting.AppSubURL + "/admin/default-hooks"
|
||||
def["BaseLink"] = setting.AppSubURL + "/-/admin/hooks"
|
||||
def["BaseLinkNew"] = setting.AppSubURL + "/-/admin/default-hooks"
|
||||
if err != nil {
|
||||
ctx.ServerError("GetWebhooksAdmin", err)
|
||||
return
|
||||
@ -67,5 +67,5 @@ func DeleteDefaultOrSystemWebhook(ctx *context.Context) {
|
||||
ctx.Flash.Success(ctx.Tr("repo.settings.webhook_deletion_success"))
|
||||
}
|
||||
|
||||
ctx.JSONRedirect(setting.AppSubURL + "/admin/hooks")
|
||||
ctx.JSONRedirect(setting.AppSubURL + "/-/admin/hooks")
|
||||
}
|
||||
|
@ -74,5 +74,5 @@ func EmptyNotices(ctx *context.Context) {
|
||||
|
||||
log.Trace("System notices deleted by admin (%s): [start: %d]", ctx.Doer.Name, 0)
|
||||
ctx.Flash.Success(ctx.Tr("admin.notices.delete_success"))
|
||||
ctx.Redirect(setting.AppSubURL + "/admin/notices")
|
||||
ctx.Redirect(setting.AppSubURL + "/-/admin/notices")
|
||||
}
|
||||
|
@ -99,7 +99,7 @@ func DeletePackageVersion(ctx *context.Context) {
|
||||
}
|
||||
|
||||
ctx.Flash.Success(ctx.Tr("packages.settings.delete.success"))
|
||||
ctx.JSONRedirect(setting.AppSubURL + "/admin/packages?page=" + url.QueryEscape(ctx.FormString("page")) + "&q=" + url.QueryEscape(ctx.FormString("q")) + "&type=" + url.QueryEscape(ctx.FormString("type")))
|
||||
ctx.JSONRedirect(setting.AppSubURL + "/-/admin/packages?page=" + url.QueryEscape(ctx.FormString("page")) + "&q=" + url.QueryEscape(ctx.FormString("q")) + "&type=" + url.QueryEscape(ctx.FormString("type")))
|
||||
}
|
||||
|
||||
func CleanupExpiredData(ctx *context.Context) {
|
||||
@ -109,5 +109,5 @@ func CleanupExpiredData(ctx *context.Context) {
|
||||
}
|
||||
|
||||
ctx.Flash.Success(ctx.Tr("admin.packages.cleanup.success"))
|
||||
ctx.Redirect(setting.AppSubURL + "/admin/packages")
|
||||
ctx.Redirect(setting.AppSubURL + "/-/admin/packages")
|
||||
}
|
||||
|
@ -53,7 +53,7 @@ func QueueSet(ctx *context.Context) {
|
||||
maxNumber, err = strconv.Atoi(maxNumberStr)
|
||||
if err != nil {
|
||||
ctx.Flash.Error(ctx.Tr("admin.monitor.queue.settings.maxnumberworkers.error"))
|
||||
ctx.Redirect(setting.AppSubURL + "/admin/monitor/queue/" + strconv.FormatInt(qid, 10))
|
||||
ctx.Redirect(setting.AppSubURL + "/-/admin/monitor/queue/" + strconv.FormatInt(qid, 10))
|
||||
return
|
||||
}
|
||||
if maxNumber < -1 {
|
||||
@ -65,7 +65,7 @@ func QueueSet(ctx *context.Context) {
|
||||
|
||||
mq.SetWorkerMaxNumber(maxNumber)
|
||||
ctx.Flash.Success(ctx.Tr("admin.monitor.queue.settings.changed"))
|
||||
ctx.Redirect(setting.AppSubURL + "/admin/monitor/queue/" + strconv.FormatInt(qid, 10))
|
||||
ctx.Redirect(setting.AppSubURL + "/-/admin/monitor/queue/" + strconv.FormatInt(qid, 10))
|
||||
}
|
||||
|
||||
func QueueRemoveAllItems(ctx *context.Context) {
|
||||
@ -85,5 +85,5 @@ func QueueRemoveAllItems(ctx *context.Context) {
|
||||
}
|
||||
|
||||
ctx.Flash.Success(ctx.Tr("admin.monitor.queue.settings.remove_all_items_done"))
|
||||
ctx.Redirect(setting.AppSubURL + "/admin/monitor/queue/" + strconv.FormatInt(qid, 10))
|
||||
ctx.Redirect(setting.AppSubURL + "/-/admin/monitor/queue/" + strconv.FormatInt(qid, 10))
|
||||
}
|
||||
|
@ -58,7 +58,7 @@ func DeleteRepo(ctx *context.Context) {
|
||||
log.Trace("Repository deleted: %s", repo.FullName())
|
||||
|
||||
ctx.Flash.Success(ctx.Tr("repo.settings.deletion_success"))
|
||||
ctx.JSONRedirect(setting.AppSubURL + "/admin/repos?page=" + url.QueryEscape(ctx.FormString("page")) + "&sort=" + url.QueryEscape(ctx.FormString("sort")))
|
||||
ctx.JSONRedirect(setting.AppSubURL + "/-/admin/repos?page=" + url.QueryEscape(ctx.FormString("page")) + "&sort=" + url.QueryEscape(ctx.FormString("sort")))
|
||||
}
|
||||
|
||||
// UnadoptedRepos lists the unadopted repositories
|
||||
@ -114,7 +114,7 @@ func AdoptOrDeleteRepository(ctx *context.Context) {
|
||||
|
||||
dirSplit := strings.SplitN(dir, "/", 2)
|
||||
if len(dirSplit) != 2 {
|
||||
ctx.Redirect(setting.AppSubURL + "/admin/repos")
|
||||
ctx.Redirect(setting.AppSubURL + "/-/admin/repos")
|
||||
return
|
||||
}
|
||||
|
||||
@ -122,7 +122,7 @@ func AdoptOrDeleteRepository(ctx *context.Context) {
|
||||
if err != nil {
|
||||
if user_model.IsErrUserNotExist(err) {
|
||||
log.Debug("User does not exist: %s", dirSplit[0])
|
||||
ctx.Redirect(setting.AppSubURL + "/admin/repos")
|
||||
ctx.Redirect(setting.AppSubURL + "/-/admin/repos")
|
||||
return
|
||||
}
|
||||
ctx.ServerError("GetUserByName", err)
|
||||
@ -160,5 +160,5 @@ func AdoptOrDeleteRepository(ctx *context.Context) {
|
||||
}
|
||||
ctx.Flash.Success(ctx.Tr("repo.delete_preexisting_success", dir))
|
||||
}
|
||||
ctx.Redirect(setting.AppSubURL + "/admin/repos/unadopted?search=true&q=" + url.QueryEscape(q) + "&page=" + url.QueryEscape(page))
|
||||
ctx.Redirect(setting.AppSubURL + "/-/admin/repos/unadopted?search=true&q=" + url.QueryEscape(q) + "&page=" + url.QueryEscape(page))
|
||||
}
|
||||
|
@ -9,5 +9,5 @@ import (
|
||||
)
|
||||
|
||||
func RedirectToDefaultSetting(ctx *context.Context) {
|
||||
ctx.Redirect(setting.AppSubURL + "/admin/actions/runners")
|
||||
ctx.Redirect(setting.AppSubURL + "/-/admin/actions/runners")
|
||||
}
|
||||
|
@ -42,5 +42,5 @@ func Stacktrace(ctx *context.Context) {
|
||||
func StacktraceCancel(ctx *context.Context) {
|
||||
pid := ctx.PathParam("pid")
|
||||
process.GetManager().Cancel(process.IDType(pid))
|
||||
ctx.JSONRedirect(setting.AppSubURL + "/admin/monitor/stacktrace")
|
||||
ctx.JSONRedirect(setting.AppSubURL + "/-/admin/monitor/stacktrace")
|
||||
}
|
||||
|
@ -215,14 +215,14 @@ func NewUserPost(ctx *context.Context) {
|
||||
}
|
||||
|
||||
ctx.Flash.Success(ctx.Tr("admin.users.new_success", u.Name))
|
||||
ctx.Redirect(setting.AppSubURL + "/admin/users/" + strconv.FormatInt(u.ID, 10))
|
||||
ctx.Redirect(setting.AppSubURL + "/-/admin/users/" + strconv.FormatInt(u.ID, 10))
|
||||
}
|
||||
|
||||
func prepareUserInfo(ctx *context.Context) *user_model.User {
|
||||
u, err := user_model.GetUserByID(ctx, ctx.PathParamInt64(":userid"))
|
||||
if err != nil {
|
||||
if user_model.IsErrUserNotExist(err) {
|
||||
ctx.Redirect(setting.AppSubURL + "/admin/users")
|
||||
ctx.Redirect(setting.AppSubURL + "/-/admin/users")
|
||||
} else {
|
||||
ctx.ServerError("GetUserByID", err)
|
||||
}
|
||||
@ -481,7 +481,7 @@ func EditUserPost(ctx *context.Context) {
|
||||
}
|
||||
|
||||
ctx.Flash.Success(ctx.Tr("admin.users.update_profile_success"))
|
||||
ctx.Redirect(setting.AppSubURL + "/admin/users/" + url.PathEscape(ctx.PathParam(":userid")))
|
||||
ctx.Redirect(setting.AppSubURL + "/-/admin/users/" + url.PathEscape(ctx.PathParam(":userid")))
|
||||
}
|
||||
|
||||
// DeleteUser response for deleting a user
|
||||
@ -495,7 +495,7 @@ func DeleteUser(ctx *context.Context) {
|
||||
// admin should not delete themself
|
||||
if u.ID == ctx.Doer.ID {
|
||||
ctx.Flash.Error(ctx.Tr("admin.users.cannot_delete_self"))
|
||||
ctx.Redirect(setting.AppSubURL + "/admin/users/" + url.PathEscape(ctx.PathParam(":userid")))
|
||||
ctx.Redirect(setting.AppSubURL + "/-/admin/users/" + url.PathEscape(ctx.PathParam(":userid")))
|
||||
return
|
||||
}
|
||||
|
||||
@ -503,16 +503,16 @@ func DeleteUser(ctx *context.Context) {
|
||||
switch {
|
||||
case models.IsErrUserOwnRepos(err):
|
||||
ctx.Flash.Error(ctx.Tr("admin.users.still_own_repo"))
|
||||
ctx.Redirect(setting.AppSubURL + "/admin/users/" + url.PathEscape(ctx.PathParam(":userid")))
|
||||
ctx.Redirect(setting.AppSubURL + "/-/admin/users/" + url.PathEscape(ctx.PathParam(":userid")))
|
||||
case models.IsErrUserHasOrgs(err):
|
||||
ctx.Flash.Error(ctx.Tr("admin.users.still_has_org"))
|
||||
ctx.Redirect(setting.AppSubURL + "/admin/users/" + url.PathEscape(ctx.PathParam(":userid")))
|
||||
ctx.Redirect(setting.AppSubURL + "/-/admin/users/" + url.PathEscape(ctx.PathParam(":userid")))
|
||||
case models.IsErrUserOwnPackages(err):
|
||||
ctx.Flash.Error(ctx.Tr("admin.users.still_own_packages"))
|
||||
ctx.Redirect(setting.AppSubURL + "/admin/users/" + url.PathEscape(ctx.PathParam(":userid")))
|
||||
ctx.Redirect(setting.AppSubURL + "/-/admin/users/" + url.PathEscape(ctx.PathParam(":userid")))
|
||||
case models.IsErrDeleteLastAdminUser(err):
|
||||
ctx.Flash.Error(ctx.Tr("auth.last_admin"))
|
||||
ctx.Redirect(setting.AppSubURL + "/admin/users/" + url.PathEscape(ctx.PathParam(":userid")))
|
||||
ctx.Redirect(setting.AppSubURL + "/-/admin/users/" + url.PathEscape(ctx.PathParam(":userid")))
|
||||
default:
|
||||
ctx.ServerError("DeleteUser", err)
|
||||
}
|
||||
@ -521,7 +521,7 @@ func DeleteUser(ctx *context.Context) {
|
||||
log.Trace("Account deleted by admin (%s): %s", ctx.Doer.Name, u.Name)
|
||||
|
||||
ctx.Flash.Success(ctx.Tr("admin.users.deletion_success"))
|
||||
ctx.Redirect(setting.AppSubURL + "/admin/users")
|
||||
ctx.Redirect(setting.AppSubURL + "/-/admin/users")
|
||||
}
|
||||
|
||||
// AvatarPost response for change user's avatar request
|
||||
@ -538,7 +538,7 @@ func AvatarPost(ctx *context.Context) {
|
||||
ctx.Flash.Success(ctx.Tr("settings.update_user_avatar_success"))
|
||||
}
|
||||
|
||||
ctx.Redirect(setting.AppSubURL + "/admin/users/" + strconv.FormatInt(u.ID, 10))
|
||||
ctx.Redirect(setting.AppSubURL + "/-/admin/users/" + strconv.FormatInt(u.ID, 10))
|
||||
}
|
||||
|
||||
// DeleteAvatar render delete avatar page
|
||||
@ -552,5 +552,5 @@ func DeleteAvatar(ctx *context.Context) {
|
||||
ctx.Flash.Error(err.Error())
|
||||
}
|
||||
|
||||
ctx.JSONRedirect(setting.AppSubURL + "/admin/users/" + strconv.FormatInt(u.ID, 10))
|
||||
ctx.JSONRedirect(setting.AppSubURL + "/-/admin/users/" + strconv.FormatInt(u.ID, 10))
|
||||
}
|
||||
|
@ -76,7 +76,7 @@ func getRunnersCtx(ctx *context.Context) (*runnersCtx, error) {
|
||||
IsAdmin: true,
|
||||
RunnersTemplate: tplAdminRunners,
|
||||
RunnerEditTemplate: tplAdminRunnerEdit,
|
||||
RedirectLink: setting.AppSubURL + "/admin/actions/runners/",
|
||||
RedirectLink: setting.AppSubURL + "/-/admin/actions/runners/",
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -74,7 +74,7 @@ func getVariablesCtx(ctx *context.Context) (*variablesCtx, error) {
|
||||
RepoID: 0,
|
||||
IsGlobal: true,
|
||||
VariablesTemplate: tplAdminVariables,
|
||||
RedirectLink: setting.AppSubURL + "/admin/actions/variables",
|
||||
RedirectLink: setting.AppSubURL + "/-/admin/actions/variables",
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -100,8 +100,8 @@ func getOwnerRepoCtx(ctx *context.Context) (*ownerRepoCtx, error) {
|
||||
return &ownerRepoCtx{
|
||||
IsAdmin: true,
|
||||
IsSystemWebhook: ctx.PathParam(":configType") == "system-hooks",
|
||||
Link: path.Join(setting.AppSubURL, "/admin/hooks"),
|
||||
LinkNew: path.Join(setting.AppSubURL, "/admin/", ctx.PathParam(":configType")),
|
||||
Link: path.Join(setting.AppSubURL, "/-/admin/hooks"),
|
||||
LinkNew: path.Join(setting.AppSubURL, "/-/admin/", ctx.PathParam(":configType")),
|
||||
NewTemplate: tplAdminHookNew,
|
||||
}, nil
|
||||
}
|
||||
|
@ -683,7 +683,7 @@ func registerRoutes(m *web.Router) {
|
||||
adminReq := verifyAuthWithOptions(&common.VerifyOptions{SignInRequired: true, AdminRequired: true})
|
||||
|
||||
// ***** START: Admin *****
|
||||
m.Group("/admin", func() {
|
||||
m.Group("/-/admin", func() {
|
||||
m.Get("", admin.Dashboard)
|
||||
m.Get("/system_status", admin.SystemStatus)
|
||||
m.Post("", web.Bind(forms.AdminDashboardForm{}), admin.DashboardPost)
|
||||
|
Reference in New Issue
Block a user