From c3e31e295cbaefe2bcc4817e60aa58a3bc20cde5 Mon Sep 17 00:00:00 2001 From: Amy Gale Ruth Bowersox Date: Thu, 22 Jan 2026 22:46:15 -0700 Subject: [PATCH] implemented the /go shortcut processing --- database/post_link.go | 79 ++++++++++++++++++++++++++++++++++++++++++- main.go | 6 ++-- top.go | 42 ++++++++++++++++++++++- 3 files changed, 123 insertions(+), 4 deletions(-) diff --git a/database/post_link.go b/database/post_link.go index f503f61..a6ed333 100644 --- a/database/post_link.go +++ b/database/post_link.go @@ -1,6 +1,6 @@ /* * Amsterdam Web Communities System - * Copyright (c) 2025 Erbosoft Metaverse Design Solutions, All Rights Reserved + * Copyright (c) 2025-2026 Erbosoft Metaverse Design Solutions, All Rights Reserved * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this @@ -92,6 +92,83 @@ func (d *PostLinkData) AsString() string { return b.String() } +/* Classify tells us what kind of post link this is and where we should interpret it from. + * Returns: + * String value indicating the scope of the link: + * "global" - Scope across the entire site. + * "community" - Scope within a community. + * "conference" - Scope within a conference. + * "topic" - Scope within a specific topic. + * Empty string - Null link. + * String value indicating what the link points to: + * "community" - Points to a community. + * "conference" - Points to a conference. + * "topic" - Points to a topic. + * "post" - Points to a single post within the topic. + * "postrange" - Points to a range of posts within the topic. + * "postopenrange" - Points to an open-ended range of posts within the topic. + * Empty string - Null link. + */ +func (d *PostLinkData) Classify() (string, string) { + if d.Community == "" { + if d.Conference == "" { + if d.Topic == -1 { + if d.FirstPost == -1 { + return "", "" + } else if d.LastPost == -1 { + return "topic", "postopenrange" + } else if d.LastPost == d.FirstPost { + return "topic", "post" + } else { + return "topic", "postrange" + } + } else { + if d.FirstPost == -1 { + return "conference", "topic" + } else if d.LastPost == -1 { + return "conference", "postopenrange" + } else if d.LastPost == d.FirstPost { + return "conference", "post" + } else { + return "conference", "postrange" + } + } + } else { + if d.Topic == -1 { + return "community", "conference" + } else { + if d.FirstPost == -1 { + return "community", "topic" + } else if d.LastPost == -1 { + return "community", "postopenrange" + } else if d.LastPost == d.FirstPost { + return "community", "post" + } else { + return "community", "postrange" + } + } + } + } else { + if d.Conference == "" { + return "global", "community" + } else { + if d.Topic == -1 { + return "global", "conference" + } else { + if d.FirstPost == -1 { + return "global", "topic" + } else if d.LastPost == -1 { + return "global", "postopenrange" + } else if d.LastPost == d.FirstPost { + return "global", "post" + } else { + return "global", "postrange" + } + } + } + } +} + // Maximum lengths of the components. const ( maxLinkLength = 130 diff --git a/main.go b/main.go index d02386c..1d1d286 100644 --- a/main.go +++ b/main.go @@ -50,7 +50,7 @@ func setupEcho() *echo.Echo { e.POST("/TODO/*", fn) e.GET("/img/*", ui.AmServeImage) e.GET("/static/*", ui.AmStaticFileHandler()) - e.GET("/go/:postlink", fn) + e.GET("/go/:postlink", ui.AmWrap(JumpToShortcut)) e.GET("/", ui.AmWrap(TopPage)) e.GET("/about", ui.AmWrap(AboutPage)) @@ -79,7 +79,9 @@ func setupEcho() *echo.Echo { // community group commGroup := e.Group("/comm/:cid", ui.SetCommunity) - commGroup.GET("/profile", ui.AmWrap(ShowCommunity)) + fn1 := ui.AmWrap(ShowCommunity) + commGroup.GET("", fn1) + commGroup.GET("/profile", fn1) commGroup.GET("/join", ui.AmWrap(JoinCommunity)) commGroup.POST("/join", ui.AmWrap(JoinCommunityWithKey)) commGroup.GET("/unjoin", ui.AmWrap(UnjoinCommunity)) diff --git a/top.go b/top.go index 8cc66e6..2228544 100644 --- a/top.go +++ b/top.go @@ -1,6 +1,6 @@ /* * Amsterdam Web Communities System - * Copyright (c) 2025 Erbosoft Metaverse Design Solutions, All Rights Reserved + * Copyright (c) 2025-2026 Erbosoft Metaverse Design Solutions, All Rights Reserved * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this @@ -11,6 +11,7 @@ package main import ( "fmt" + "net/http" "git.erbosoft.com/amy/amsterdam/database" "git.erbosoft.com/amy/amsterdam/ui" @@ -210,3 +211,42 @@ func AboutPage(ctxt ui.AmContext) (string, any, error) { ctxt.VarMap().Set("amsterdam_pageTitle", "About Amsterdam") return "framed_template", "about.jet", nil } + +/* JumpToShortcut resolves "/go" links by redirecting them to the appropriate page. + * Parameters: + * ctxt - The AmContext for the request. + * Returns: + * Command string dictating what to be rendered. + * Data as a parameter for the command string. + * Standard Go error status. + */ +func JumpToShortcut(ctxt ui.AmContext) (string, any, error) { + link, err := database.AmDecodePostLink(ctxt.URLParam("postlink")) + if err != nil { + ctxt.SetRC(http.StatusNotFound) + return ui.ErrorPage(ctxt, fmt.Errorf("not found: %s (%v)", ctxt.URLParam("postlink"), err)) + } + scope, target := link.Classify() + if scope != "global" { + ctxt.SetRC(http.StatusNotFound) + return ui.ErrorPage(ctxt, fmt.Errorf("not found: %s", ctxt.URLParam("postlink"))) + } + if err = link.VerifyNames(ctxt.Ctx()); err != nil { + ctxt.SetRC(http.StatusNotFound) + return ui.ErrorPage(ctxt, fmt.Errorf("not found: %s (%v)", ctxt.URLParam("postlink"), err)) + } + targetURL := "" + switch target { + case "community": + targetURL = fmt.Sprintf("/comm/%s", link.Community) + case "conference": + targetURL = fmt.Sprintf("/comm/%s/conf/%s", link.Community, link.Conference) + case "topic": + targetURL = fmt.Sprintf("/comm/%s/conf/%s/r/%d", link.Community, link.Conference, link.Topic) + case "post", "postrange", "postopenrange": + targetURL = fmt.Sprintf("/comm/%s/conf/%s/r/%d?r=%d,%d", link.Community, link.Conference, link.Topic, link.FirstPost, link.LastPost) + default: + return ui.ErrorPage(ctxt, fmt.Errorf("invalid target '%s' for link: %s", target, ctxt.URLParam("postlink"))) + } + return "redirect", targetURL, nil +}