mirror of
https://github.com/osmarks/mycorrhiza.git
synced 2025-06-07 13:24:06 +00:00
Be more reasonable with trailing slashes
This commit is contained in:
parent
01f7791d4a
commit
e5107252d1
@ -19,7 +19,7 @@ func initAdmin() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func handlerAdmin(w http.ResponseWriter, rq *http.Request) {
|
func handlerAdmin(w http.ResponseWriter, rq *http.Request) {
|
||||||
log.Println(rq.URL)
|
prepareRq(rq)
|
||||||
if user.CanProceed(rq, "admin") {
|
if user.CanProceed(rq, "admin") {
|
||||||
w.Header().Set("Content-Type", "text/html;charset=utf-8")
|
w.Header().Set("Content-Type", "text/html;charset=utf-8")
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
@ -28,14 +28,14 @@ func handlerAdmin(w http.ResponseWriter, rq *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func handlerAdminShutdown(w http.ResponseWriter, rq *http.Request) {
|
func handlerAdminShutdown(w http.ResponseWriter, rq *http.Request) {
|
||||||
log.Println(rq.URL)
|
prepareRq(rq)
|
||||||
if user.CanProceed(rq, "admin/shutdown") && rq.Method == "POST" {
|
if user.CanProceed(rq, "admin/shutdown") && rq.Method == "POST" {
|
||||||
log.Fatal("An admin commanded the wiki to shutdown")
|
log.Fatal("An admin commanded the wiki to shutdown")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func handlerAdminReindexUsers(w http.ResponseWriter, rq *http.Request) {
|
func handlerAdminReindexUsers(w http.ResponseWriter, rq *http.Request) {
|
||||||
log.Println(rq.URL)
|
prepareRq(rq)
|
||||||
if user.CanProceed(rq, "admin") && rq.Method == "POST" {
|
if user.CanProceed(rq, "admin") && rq.Method == "POST" {
|
||||||
user.ReadUsersFromFilesystem()
|
user.ReadUsersFromFilesystem()
|
||||||
http.Redirect(w, rq, "/hypha/"+util.UserHypha, http.StatusSeeOther)
|
http.Redirect(w, rq, "/hypha/"+util.UserHypha, http.StatusSeeOther)
|
||||||
|
@ -19,7 +19,7 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func handlerRegister(w http.ResponseWriter, rq *http.Request) {
|
func handlerRegister(w http.ResponseWriter, rq *http.Request) {
|
||||||
log.Println(rq.URL)
|
prepareRq(rq)
|
||||||
if !util.UseRegistration {
|
if !util.UseRegistration {
|
||||||
w.WriteHeader(http.StatusForbidden)
|
w.WriteHeader(http.StatusForbidden)
|
||||||
}
|
}
|
||||||
@ -69,7 +69,7 @@ func handlerLogoutConfirm(w http.ResponseWriter, rq *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func handlerLoginData(w http.ResponseWriter, rq *http.Request) {
|
func handlerLoginData(w http.ResponseWriter, rq *http.Request) {
|
||||||
log.Println(rq.URL)
|
prepareRq(rq)
|
||||||
var (
|
var (
|
||||||
username = util.CanonicalName(rq.PostFormValue("username"))
|
username = util.CanonicalName(rq.PostFormValue("username"))
|
||||||
password = rq.PostFormValue("password")
|
password = rq.PostFormValue("password")
|
||||||
@ -83,7 +83,7 @@ func handlerLoginData(w http.ResponseWriter, rq *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func handlerLogin(w http.ResponseWriter, rq *http.Request) {
|
func handlerLogin(w http.ResponseWriter, rq *http.Request) {
|
||||||
log.Println(rq.URL)
|
prepareRq(rq)
|
||||||
w.Header().Set("Content-Type", "text/html;charset=utf-8")
|
w.Header().Set("Content-Type", "text/html;charset=utf-8")
|
||||||
if user.AuthUsed {
|
if user.AuthUsed {
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
|
@ -23,7 +23,7 @@ func init() {
|
|||||||
|
|
||||||
// handlerHistory lists all revisions of a hypha
|
// handlerHistory lists all revisions of a hypha
|
||||||
func handlerHistory(w http.ResponseWriter, rq *http.Request) {
|
func handlerHistory(w http.ResponseWriter, rq *http.Request) {
|
||||||
log.Println(rq.URL)
|
prepareRq(rq)
|
||||||
hyphaName := HyphaNameFromRq(rq, "history")
|
hyphaName := HyphaNameFromRq(rq, "history")
|
||||||
var list string
|
var list string
|
||||||
|
|
||||||
@ -40,7 +40,7 @@ func handlerHistory(w http.ResponseWriter, rq *http.Request) {
|
|||||||
|
|
||||||
// Recent changes
|
// Recent changes
|
||||||
func handlerRecentChanges(w http.ResponseWriter, rq *http.Request) {
|
func handlerRecentChanges(w http.ResponseWriter, rq *http.Request) {
|
||||||
log.Println(rq.URL)
|
prepareRq(rq)
|
||||||
var (
|
var (
|
||||||
noPrefix = strings.TrimPrefix(rq.URL.String(), "/recent-changes/")
|
noPrefix = strings.TrimPrefix(rq.URL.String(), "/recent-changes/")
|
||||||
n, err = strconv.Atoi(noPrefix)
|
n, err = strconv.Atoi(noPrefix)
|
||||||
@ -53,7 +53,7 @@ func handlerRecentChanges(w http.ResponseWriter, rq *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func genericHandlerOfFeeds(w http.ResponseWriter, rq *http.Request, f func() (string, error), name string) {
|
func genericHandlerOfFeeds(w http.ResponseWriter, rq *http.Request, f func() (string, error), name string) {
|
||||||
log.Println(rq.URL)
|
prepareRq(rq)
|
||||||
if content, err := f(); err != nil {
|
if content, err := f(); err != nil {
|
||||||
w.Header().Set("Content-Type", "text/plain;charset=utf-8")
|
w.Header().Set("Content-Type", "text/plain;charset=utf-8")
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
@ -35,7 +35,7 @@ func factoryHandlerAsker(
|
|||||||
succPageTemplate func(*http.Request, string, bool) string,
|
succPageTemplate func(*http.Request, string, bool) string,
|
||||||
) func(http.ResponseWriter, *http.Request) {
|
) func(http.ResponseWriter, *http.Request) {
|
||||||
return func(w http.ResponseWriter, rq *http.Request) {
|
return func(w http.ResponseWriter, rq *http.Request) {
|
||||||
log.Println(rq.URL)
|
prepareRq(rq)
|
||||||
var (
|
var (
|
||||||
hyphaName = HyphaNameFromRq(rq, actionPath)
|
hyphaName = HyphaNameFromRq(rq, actionPath)
|
||||||
h = hyphae.ByName(hyphaName)
|
h = hyphae.ByName(hyphaName)
|
||||||
@ -85,7 +85,7 @@ func factoryHandlerConfirmer(
|
|||||||
confirmer func(*hyphae.Hypha, *user.User, *http.Request) (*history.HistoryOp, string),
|
confirmer func(*hyphae.Hypha, *user.User, *http.Request) (*history.HistoryOp, string),
|
||||||
) func(http.ResponseWriter, *http.Request) {
|
) func(http.ResponseWriter, *http.Request) {
|
||||||
return func(w http.ResponseWriter, rq *http.Request) {
|
return func(w http.ResponseWriter, rq *http.Request) {
|
||||||
log.Println(rq.URL)
|
prepareRq(rq)
|
||||||
var (
|
var (
|
||||||
hyphaName = HyphaNameFromRq(rq, actionPath)
|
hyphaName = HyphaNameFromRq(rq, actionPath)
|
||||||
h = hyphae.ByName(hyphaName)
|
h = hyphae.ByName(hyphaName)
|
||||||
@ -129,7 +129,7 @@ var handlerRenameConfirm = factoryHandlerConfirmer(
|
|||||||
|
|
||||||
// handlerEdit shows the edit form. It doesn't edit anything actually.
|
// handlerEdit shows the edit form. It doesn't edit anything actually.
|
||||||
func handlerEdit(w http.ResponseWriter, rq *http.Request) {
|
func handlerEdit(w http.ResponseWriter, rq *http.Request) {
|
||||||
log.Println(rq.URL)
|
prepareRq(rq)
|
||||||
var (
|
var (
|
||||||
hyphaName = HyphaNameFromRq(rq, "edit")
|
hyphaName = HyphaNameFromRq(rq, "edit")
|
||||||
h = hyphae.ByName(hyphaName)
|
h = hyphae.ByName(hyphaName)
|
||||||
@ -166,7 +166,7 @@ func handlerEdit(w http.ResponseWriter, rq *http.Request) {
|
|||||||
|
|
||||||
// handlerUploadText uploads a new text part for the hypha.
|
// handlerUploadText uploads a new text part for the hypha.
|
||||||
func handlerUploadText(w http.ResponseWriter, rq *http.Request) {
|
func handlerUploadText(w http.ResponseWriter, rq *http.Request) {
|
||||||
log.Println(rq.URL)
|
prepareRq(rq)
|
||||||
var (
|
var (
|
||||||
hyphaName = HyphaNameFromRq(rq, "upload-text")
|
hyphaName = HyphaNameFromRq(rq, "upload-text")
|
||||||
h = hyphae.ByName(hyphaName)
|
h = hyphae.ByName(hyphaName)
|
||||||
@ -206,7 +206,7 @@ func handlerUploadText(w http.ResponseWriter, rq *http.Request) {
|
|||||||
|
|
||||||
// handlerUploadBinary uploads a new binary part for the hypha.
|
// handlerUploadBinary uploads a new binary part for the hypha.
|
||||||
func handlerUploadBinary(w http.ResponseWriter, rq *http.Request) {
|
func handlerUploadBinary(w http.ResponseWriter, rq *http.Request) {
|
||||||
log.Println(rq.URL)
|
prepareRq(rq)
|
||||||
rq.ParseMultipartForm(10 << 20) // Set upload limit
|
rq.ParseMultipartForm(10 << 20) // Set upload limit
|
||||||
var (
|
var (
|
||||||
hyphaName = HyphaNameFromRq(rq, "upload-binary")
|
hyphaName = HyphaNameFromRq(rq, "upload-binary")
|
||||||
|
@ -29,7 +29,7 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func handlerAttachment(w http.ResponseWriter, rq *http.Request) {
|
func handlerAttachment(w http.ResponseWriter, rq *http.Request) {
|
||||||
log.Println(rq.URL)
|
prepareRq(rq)
|
||||||
var (
|
var (
|
||||||
hyphaName = HyphaNameFromRq(rq, "attachment")
|
hyphaName = HyphaNameFromRq(rq, "attachment")
|
||||||
h = hyphae.ByName(hyphaName)
|
h = hyphae.ByName(hyphaName)
|
||||||
@ -43,7 +43,7 @@ func handlerAttachment(w http.ResponseWriter, rq *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func handlerPrimitiveDiff(w http.ResponseWriter, rq *http.Request) {
|
func handlerPrimitiveDiff(w http.ResponseWriter, rq *http.Request) {
|
||||||
log.Println(rq.URL)
|
prepareRq(rq)
|
||||||
var (
|
var (
|
||||||
shorterUrl = strings.TrimPrefix(rq.URL.Path, "/primitive-diff/")
|
shorterUrl = strings.TrimPrefix(rq.URL.Path, "/primitive-diff/")
|
||||||
firstSlashIndex = strings.IndexRune(shorterUrl, '/')
|
firstSlashIndex = strings.IndexRune(shorterUrl, '/')
|
||||||
@ -61,7 +61,7 @@ func handlerPrimitiveDiff(w http.ResponseWriter, rq *http.Request) {
|
|||||||
|
|
||||||
// handlerRevision displays a specific revision of text part a page
|
// handlerRevision displays a specific revision of text part a page
|
||||||
func handlerRevision(w http.ResponseWriter, rq *http.Request) {
|
func handlerRevision(w http.ResponseWriter, rq *http.Request) {
|
||||||
log.Println(rq.URL)
|
prepareRq(rq)
|
||||||
var (
|
var (
|
||||||
shorterUrl = strings.TrimPrefix(rq.URL.Path, "/rev/")
|
shorterUrl = strings.TrimPrefix(rq.URL.Path, "/rev/")
|
||||||
firstSlashIndex = strings.IndexRune(shorterUrl, '/')
|
firstSlashIndex = strings.IndexRune(shorterUrl, '/')
|
||||||
@ -88,7 +88,7 @@ func handlerRevision(w http.ResponseWriter, rq *http.Request) {
|
|||||||
|
|
||||||
// handlerText serves raw source text of the hypha.
|
// handlerText serves raw source text of the hypha.
|
||||||
func handlerText(w http.ResponseWriter, rq *http.Request) {
|
func handlerText(w http.ResponseWriter, rq *http.Request) {
|
||||||
log.Println(rq.URL)
|
prepareRq(rq)
|
||||||
hyphaName := HyphaNameFromRq(rq, "text")
|
hyphaName := HyphaNameFromRq(rq, "text")
|
||||||
if h := hyphae.ByName(hyphaName); h.Exists {
|
if h := hyphae.ByName(hyphaName); h.Exists {
|
||||||
log.Println("Serving", h.TextPath)
|
log.Println("Serving", h.TextPath)
|
||||||
@ -99,7 +99,7 @@ func handlerText(w http.ResponseWriter, rq *http.Request) {
|
|||||||
|
|
||||||
// handlerBinary serves binary part of the hypha.
|
// handlerBinary serves binary part of the hypha.
|
||||||
func handlerBinary(w http.ResponseWriter, rq *http.Request) {
|
func handlerBinary(w http.ResponseWriter, rq *http.Request) {
|
||||||
log.Println(rq.URL)
|
prepareRq(rq)
|
||||||
hyphaName := HyphaNameFromRq(rq, "binary")
|
hyphaName := HyphaNameFromRq(rq, "binary")
|
||||||
if h := hyphae.ByName(hyphaName); h.Exists {
|
if h := hyphae.ByName(hyphaName); h.Exists {
|
||||||
log.Println("Serving", h.BinaryPath)
|
log.Println("Serving", h.BinaryPath)
|
||||||
@ -110,7 +110,7 @@ func handlerBinary(w http.ResponseWriter, rq *http.Request) {
|
|||||||
|
|
||||||
// handlerHypha is the main hypha action that displays the hypha and the binary upload form along with some navigation.
|
// handlerHypha is the main hypha action that displays the hypha and the binary upload form along with some navigation.
|
||||||
func handlerHypha(w http.ResponseWriter, rq *http.Request) {
|
func handlerHypha(w http.ResponseWriter, rq *http.Request) {
|
||||||
log.Println(rq.URL)
|
prepareRq(rq)
|
||||||
var (
|
var (
|
||||||
hyphaName = HyphaNameFromRq(rq, "page", "hypha")
|
hyphaName = HyphaNameFromRq(rq, "page", "hypha")
|
||||||
h = hyphae.ByName(hyphaName)
|
h = hyphae.ByName(hyphaName)
|
||||||
|
29
main.go
29
main.go
@ -48,7 +48,7 @@ func HttpErr(w http.ResponseWriter, status int, name, title, errMsg string) {
|
|||||||
|
|
||||||
// Show all hyphae
|
// Show all hyphae
|
||||||
func handlerList(w http.ResponseWriter, rq *http.Request) {
|
func handlerList(w http.ResponseWriter, rq *http.Request) {
|
||||||
log.Println(rq.URL)
|
prepareRq(rq)
|
||||||
util.HTTP200Page(w, base("List of pages", views.HyphaListHTML(), user.FromRequest(rq)))
|
util.HTTP200Page(w, base("List of pages", views.HyphaListHTML(), user.FromRequest(rq)))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,7 +57,7 @@ var base = views.BaseHTML
|
|||||||
|
|
||||||
// Reindex all hyphae by checking the wiki storage directory anew.
|
// Reindex all hyphae by checking the wiki storage directory anew.
|
||||||
func handlerReindex(w http.ResponseWriter, rq *http.Request) {
|
func handlerReindex(w http.ResponseWriter, rq *http.Request) {
|
||||||
log.Println(rq.URL)
|
prepareRq(rq)
|
||||||
if ok := user.CanProceed(rq, "reindex"); !ok {
|
if ok := user.CanProceed(rq, "reindex"); !ok {
|
||||||
HttpErr(w, http.StatusForbidden, util.HomePage, "Not enough rights", "You must be an admin to reindex hyphae.")
|
HttpErr(w, http.StatusForbidden, util.HomePage, "Not enough rights", "You must be an admin to reindex hyphae.")
|
||||||
log.Println("Rejected", rq.URL)
|
log.Println("Rejected", rq.URL)
|
||||||
@ -75,7 +75,7 @@ func handlerReindex(w http.ResponseWriter, rq *http.Request) {
|
|||||||
|
|
||||||
// Update header links by reading the configured hypha, if there is any, or resorting to default values.
|
// Update header links by reading the configured hypha, if there is any, or resorting to default values.
|
||||||
func handlerUpdateHeaderLinks(w http.ResponseWriter, rq *http.Request) {
|
func handlerUpdateHeaderLinks(w http.ResponseWriter, rq *http.Request) {
|
||||||
log.Println(rq.URL)
|
prepareRq(rq)
|
||||||
if ok := user.CanProceed(rq, "update-header-links"); !ok {
|
if ok := user.CanProceed(rq, "update-header-links"); !ok {
|
||||||
HttpErr(w, http.StatusForbidden, util.HomePage, "Not enough rights", "You must be a moderator to update header links.")
|
HttpErr(w, http.StatusForbidden, util.HomePage, "Not enough rights", "You must be a moderator to update header links.")
|
||||||
log.Println("Rejected", rq.URL)
|
log.Println("Rejected", rq.URL)
|
||||||
@ -87,7 +87,7 @@ func handlerUpdateHeaderLinks(w http.ResponseWriter, rq *http.Request) {
|
|||||||
|
|
||||||
// Redirect to a random hypha.
|
// Redirect to a random hypha.
|
||||||
func handlerRandom(w http.ResponseWriter, rq *http.Request) {
|
func handlerRandom(w http.ResponseWriter, rq *http.Request) {
|
||||||
log.Println(rq.URL)
|
prepareRq(rq)
|
||||||
var (
|
var (
|
||||||
randomHyphaName string
|
randomHyphaName string
|
||||||
amountOfHyphae int = hyphae.Count()
|
amountOfHyphae int = hyphae.Count()
|
||||||
@ -108,7 +108,7 @@ func handlerRandom(w http.ResponseWriter, rq *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func handlerStyle(w http.ResponseWriter, rq *http.Request) {
|
func handlerStyle(w http.ResponseWriter, rq *http.Request) {
|
||||||
log.Println(rq.URL)
|
prepareRq(rq)
|
||||||
if _, err := os.Stat(util.WikiDir + "/static/common.css"); err == nil {
|
if _, err := os.Stat(util.WikiDir + "/static/common.css"); err == nil {
|
||||||
http.ServeFile(w, rq, util.WikiDir+"/static/common.css")
|
http.ServeFile(w, rq, util.WikiDir+"/static/common.css")
|
||||||
} else {
|
} else {
|
||||||
@ -121,7 +121,7 @@ func handlerStyle(w http.ResponseWriter, rq *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func handlerToolbar(w http.ResponseWriter, rq *http.Request) {
|
func handlerToolbar(w http.ResponseWriter, rq *http.Request) {
|
||||||
log.Println(rq.URL)
|
prepareRq(rq)
|
||||||
w.Header().Set("Content-Type", "text/javascript;charset=utf-8")
|
w.Header().Set("Content-Type", "text/javascript;charset=utf-8")
|
||||||
w.Write([]byte(assets.ToolbarJS()))
|
w.Write([]byte(assets.ToolbarJS()))
|
||||||
}
|
}
|
||||||
@ -178,6 +178,11 @@ Disallow: /
|
|||||||
Crawl-delay: 5`))
|
Crawl-delay: 5`))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func prepareRq(rq *http.Request) {
|
||||||
|
log.Println(rq.RequestURI)
|
||||||
|
rq.URL.Path = strings.TrimSuffix(rq.URL.Path, "/")
|
||||||
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
parseCliArgs()
|
parseCliArgs()
|
||||||
|
|
||||||
@ -210,12 +215,12 @@ func main() {
|
|||||||
// See http_mutators.go for /upload-binary/, /upload-text/, /edit/, /delete-ask/, /delete-confirm/, /rename-ask/, /rename-confirm/, /unattach-ask/, /unattach-confirm/
|
// See http_mutators.go for /upload-binary/, /upload-text/, /edit/, /delete-ask/, /delete-confirm/, /rename-ask/, /rename-confirm/, /unattach-ask/, /unattach-confirm/
|
||||||
// See http_auth.go for /login, /login-data, /logout, /logout-confirm
|
// See http_auth.go for /login, /login-data, /logout, /logout-confirm
|
||||||
// See http_history.go for /history/, /recent-changes
|
// See http_history.go for /history/, /recent-changes
|
||||||
http.HandleFunc("/list", handlerList)
|
http.HandleFunc("/list/", handlerList)
|
||||||
http.HandleFunc("/reindex", handlerReindex)
|
http.HandleFunc("/reindex/", handlerReindex)
|
||||||
http.HandleFunc("/update-header-links", handlerUpdateHeaderLinks)
|
http.HandleFunc("/update-header-links/", handlerUpdateHeaderLinks)
|
||||||
http.HandleFunc("/random", handlerRandom)
|
http.HandleFunc("/random/", handlerRandom)
|
||||||
http.HandleFunc("/about", handlerAbout)
|
http.HandleFunc("/about/", handlerAbout)
|
||||||
http.HandleFunc("/user-list", handlerUserList)
|
http.HandleFunc("/user-list/", handlerUserList)
|
||||||
http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir(WikiDir+"/static"))))
|
http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir(WikiDir+"/static"))))
|
||||||
http.HandleFunc("/favicon.ico", func(w http.ResponseWriter, rq *http.Request) {
|
http.HandleFunc("/favicon.ico", func(w http.ResponseWriter, rq *http.Request) {
|
||||||
http.ServeFile(w, rq, WikiDir+"/static/favicon.ico")
|
http.ServeFile(w, rq, WikiDir+"/static/favicon.ico")
|
||||||
|
3
name.go
3
name.go
@ -18,7 +18,8 @@ func HyphaNameFromRq(rq *http.Request, actions ...string) string {
|
|||||||
return util.CanonicalName(strings.TrimPrefix(p, "/"+action+"/"))
|
return util.CanonicalName(strings.TrimPrefix(p, "/"+action+"/"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
panic("HyphaNameFromRq: no matching action passed")
|
log.Println("HyphaNameFromRq: this request is invalid, fallback to home hypha")
|
||||||
|
return util.HomePage
|
||||||
}
|
}
|
||||||
|
|
||||||
// geminiHyphaNameFromRq extracts hypha name from gemini request. You have to also pass the action which is embedded in the url or several actions. For url /hypha/hypha, the action would be "hypha".
|
// geminiHyphaNameFromRq extracts hypha name from gemini request. You have to also pass the action which is embedded in the url or several actions. For url /hypha/hypha, the action would be "hypha".
|
||||||
|
Loading…
x
Reference in New Issue
Block a user