26 Commits

Author SHA1 Message Date
amy 6526c74ed2 update release build action 2026-04-29 23:02:25 -06:00
amy 10a347747e set up auto build of executables and advance version number
Build and Release Amsterdam Assets / build-and-upload (release) Failing after 1m0s
2026-04-29 22:29:35 -06:00
amy 631215f6a0 library tidying 2026-04-25 22:47:12 -06:00
amy 759996b4cd extended the use of ConferenceLink throughout the conferencing code 2026-04-24 22:40:33 -06:00
amy a068e17e65 fixed up CHANGELOG and edited the TODO 2026-04-22 17:49:08 -05:00
amy 57088e4680 added Apple site icon configuration and frame - closes issue #5 2026-04-21 16:30:40 -05:00
amy 17de55c5c2 Admin can now change the user name of a user account. Closes issue #2. 2026-04-21 15:40:50 -05:00
amy ea807cc55f update TODO 2026-04-20 21:20:46 -05:00
amy 14c6df9891 cleanup for deleting community conferences 2026-04-19 20:52:38 -05:00
amy 581319279a straighten out conference alias add and remove 2026-04-19 20:45:23 -05:00
amy 0e3c1a293e fixed showing of hidden posts 2026-04-19 20:18:23 -05:00
amy 391d8ccc99 bug fixes related to top page and post link rewriting 2026-04-19 17:26:38 -05:00
amy 3deb11e0a5 changes to conference alias map handling to account for community ID 2026-04-19 09:01:59 -05:00
amy 1348d0225f correct architectural goof where conference aliases had global scope instead of community scope (untested) 2026-04-18 21:13:00 -05:00
amy 70dcf82234 update README and TODO 2026-04-17 19:46:08 -05:00
amy 8c1fa9ebdc Merge pull request 'Implemented database scripting for installation, conversion, and migration' (#4) from database-scripting into main
Reviewed-on: #4
2026-04-17 17:09:59 -06:00
amy 5a41509170 make sure to update returned version number as migration scripts are applied 2026-04-16 22:32:36 -05:00
amy 6a32db5adf fixed up filesystem reading logic for migration scripts 2026-04-16 22:30:57 -05:00
amy e55e02509b added migration script support 2026-04-16 22:25:04 -05:00
amy c08b570457 moved Venice migration script into embeds as well 2026-04-16 21:42:03 -05:00
amy c8a331d90c we can now install the scema to a blank mysql database 2026-04-16 17:23:36 -05:00
amy 056c02959a use home variable expansion so I can develop on Fountain 2026-04-16 13:31:00 -05:00
amy 27bc2f033a added first functions for database checking at startup 2026-04-16 11:58:25 -05:00
amy f4d1bb8f82 missed changing the version number :O 2026-04-11 22:59:10 -06:00
amy d408eea48b changelog for release 0.1.1 2026-04-11 22:48:14 -06:00
amy 3b298bf3fb Fixed bug in post link resolution that was causing conference alias to be set wrongg on post links. Closes issue #3. 2026-04-11 22:46:15 -06:00
38 changed files with 543 additions and 377 deletions
+33
View File
@@ -0,0 +1,33 @@
name: Build and Release Amsterdam Assets
on:
release:
types: [published]
jobs:
build-and-upload:
runs-on: host
steps:
- name: Check out code
uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: '1.26'
- name: Build Binaries
run: |
# Build targets: Linux AMD64, Linux ARM64 (RPi), Windows AMD64, macOS ARM64 (Apple Silicon), macOS AMD64 (Intel)
GOOS=linux GOARCH=amd64 go build -o amsterdam-linux-amd64
GOOS=linux GOARCH=arm64 go build -o amsterdam-linux-arm64
GOOS=windows GOARCH=amd64 go build -o amsterdam-windows-amd64.exe
GOOS=darwin GOARCH=arm64 go build -o amsterdam-macos-arm64
GOOS=darwin GOARCH=amd64 go build -o amsterdam-macos-amd64
- name: Upload Assets to Release
uses: softprops/action-gh-release@v2
with:
files: |
amsterdam-linux-amd64
amsterdam-linux-arm64
amsterdam-windows-amd64.exe
amsterdam-macos-arm64
amsterdam-macos-amd64
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+1 -1
View File
@@ -6,4 +6,4 @@ __debug_bin*
/test*.yaml /test*.yaml
# Ignore mac-specific files # Ignore mac-specific files
/.DS_Store .DS_Store
+11 -1
View File
@@ -11,6 +11,16 @@
"mode": "auto", "mode": "auto",
"program": "${workspaceFolder}" "program": "${workspaceFolder}"
}, },
{
"name": "Blank Test",
"type": "go",
"request": "launch",
"mode": "auto",
"program": "${workspaceFolder}",
"env": {
"AMSTERDAM_DATABASE_NAME": "blank"
}
},
{ {
"name": "EMinds Reborn", "name": "EMinds Reborn",
"type": "go", "type": "go",
@@ -18,7 +28,7 @@
"mode": "auto", "mode": "auto",
"program": "${workspaceFolder}", "program": "${workspaceFolder}",
"env": { "env": {
"AMSTERDAM_CONFIG": "/u01/home/amy/Erbosoft/emreborn/amsterdam.yaml" "AMSTERDAM_CONFIG": "${env:HOME}/Erbosoft/emreborn/amsterdam.yaml"
} }
}, },
] ]
+4 -7
View File
@@ -114,14 +114,9 @@ in your `PATH`. Then run `go generate` to regenerate the CSS file before you run
## Installing Amsterdam ## Installing Amsterdam
You will need a MySQL database to store Amsterdam data. Create a new empty database, then, from the command line, use the command: You will need a MySQL database to store Amsterdam data. Create a new empty database.
> `mysql -u root -p _databasename_ \< setup/mysql-database.sql` Ensure a user in your database is granted all privileges to all tables in your new database.
(Replace _databasename_ with the name of your database. If you use a user other than `root` for administrative access to your
MySQL server, use that.)
Ensure a user in your database is granted SELECT, INSERT, UPDATE, and DELETE privileges to all tables in your new database.
This is the user that you will configure Amsterdam to use. This is the user that you will configure Amsterdam to use.
The database may be specified to Amsterdam with the following command line options or environment variables: The database may be specified to Amsterdam with the following command line options or environment variables:
@@ -133,6 +128,8 @@ The database may be specified to Amsterdam with the following command line optio
All these options may also be specified via the configuration file (see below). All these options may also be specified via the configuration file (see below).
The first time you execute Amsterdam, the necessary database tables will be created and populated.
Amsterdam also requires access to a local SMTP server, as it sends out E-mail messages such as account verification, Amsterdam also requires access to a local SMTP server, as it sends out E-mail messages such as account verification,
password reminders, subscribed posts, and messages from conference or community hosts. It may be specified to Amsterdam password reminders, subscribed posts, and messages from conference or community hosts. It may be specified to Amsterdam
with the following command line options or environment variables: with the following command line options or environment variables:
+4 -19
View File
@@ -1,22 +1,10 @@
# Future Directions for Amsterdam # Future Directions for Amsterdam
After the point where it reaches feature parity with Venice circa 2006.
## Pre-Launch Polish
* ~~Policy page support.~~
* ~~User agreement in a separate file rather than directly in settings.~~
* ~~Support all customizations that were done with the EMinds instance of Venice.~~
* ~~Gitea-like status page showing Go-specific internals.~~
* ~~Build static Tailwind CSS file rather than using remote-loaded version. (Gate on debug/prod flag)~~
* ~~Rate limiter.~~
* ~~Better logging configuration.~~
## Immediate Cleanups Required ## Immediate Cleanups Required
* A better way to set up the database than `setup/database.sql`. Bring the table setup into the application somehow. * <s>A better way to set up the database than `setup/database.sql`. Bring the table setup into the application somehow.
The [migrate](https://github.com/golang-migrate/migrate) library might be of use here. The [migrate](https://github.com/golang-migrate/migrate) library might be of use here.</s>
* Database format migrations. * ~~Database format migrations.~~
* Allow use of Postgres as a database. * Allow use of Postgres as a database.
* Dockerization. * Dockerization.
* Implement proper help and online documentation. * Implement proper help and online documentation.
@@ -27,12 +15,9 @@ After the point where it reaches feature parity with Venice circa 2006.
* Should those be community "services" instead? * Should those be community "services" instead?
* For Chat, if it's implemented, it should use XMPP. * For Chat, if it's implemented, it should use XMPP.
## Architectural Goofs
* Conference Aliases are effectively in a system-wide namespace. Should be per-community.
## Additional Items ## Additional Items
* Ensure design is responsive enough that we can use the site on mobile devices.
* Decouple from MySQL, introduce other database support. Postgres and SQLite are the two priorities here. * Decouple from MySQL, introduce other database support. Postgres and SQLite are the two priorities here.
* Fix password storage. Straight SHA-1 hashes aren't gonna cut it. There are better ways. * Fix password storage. Straight SHA-1 hashes aren't gonna cut it. There are better ways.
* Introduce OAuth authentication? (Related to above) * Introduce OAuth authentication? (Related to above)
+23 -18
View File
@@ -136,7 +136,7 @@ func Topics(ctxt ui.AmContext) (string, any) {
} }
// create the "read new" URL // create the "read new" URL
urlStem := fmt.Sprintf("/comm/%s/conf/%s", comm.Alias, ctxt.GetScratch("currentAlias")) urlStem := ctxt.GetScratch("ConferenceLink").(string)
if !ctxt.CurrentUser().IsAnon { if !ctxt.CurrentUser().IsAnon {
traverser := ui.NewTopicTraverser(topics) traverser := ui.NewTopicTraverser(topics)
ctxt.SetSession("topic.traverser", traverser) ctxt.SetSession("topic.traverser", traverser)
@@ -178,14 +178,13 @@ func Topics(ctxt ui.AmContext) (string, any) {
* Data as a parameter for the command string. * Data as a parameter for the command string.
*/ */
func NewTopicForm(ctxt ui.AmContext) (string, any) { func NewTopicForm(ctxt ui.AmContext) (string, any) {
comm := ctxt.CurrentCommunity()
conf := ctxt.GetScratch("currentConference").(*database.Conference) conf := ctxt.GetScratch("currentConference").(*database.Conference)
myLevel := ctxt.GetScratch("levelInConference").(uint16) myLevel := ctxt.GetScratch("levelInConference").(uint16)
if !conf.TestPermission("Conference.Create", myLevel) { if !conf.TestPermission("Conference.Create", myLevel) {
return "error", echo.NewHTTPError(http.StatusForbidden, "you are not permitted to create topics in this conference") return "error", echo.NewHTTPError(http.StatusForbidden, "you are not permitted to create topics in this conference")
} }
ctxt.VarMap().Set("conferenceName", conf.Name) ctxt.VarMap().Set("conferenceName", conf.Name)
ctxt.VarMap().Set("urlStem", fmt.Sprintf("/comm/%s/conf/%s", comm.Alias, ctxt.GetScratch("currentAlias"))) ctxt.VarMap().Set("urlStem", ctxt.GetScratch("ConferenceLink").(string))
ctxt.VarMap().Set("topicName", "") ctxt.VarMap().Set("topicName", "")
pseud, err := conf.DefaultPseud(ctxt.Ctx(), ctxt.CurrentUser()) pseud, err := conf.DefaultPseud(ctxt.Ctx(), ctxt.CurrentUser())
if err != nil { if err != nil {
@@ -212,7 +211,7 @@ func NewTopic(ctxt ui.AmContext) (string, any) {
return "error", echo.NewHTTPError(http.StatusForbidden, "you are not permitted to create topics in this conference") return "error", echo.NewHTTPError(http.StatusForbidden, "you are not permitted to create topics in this conference")
} }
urlStem := fmt.Sprintf("/comm/%s/conf/%s", comm.Alias, ctxt.GetScratch("currentAlias")) urlStem := ctxt.GetScratch("ConferenceLink").(string)
if ctxt.FormFieldIsSet("cancel") { if ctxt.FormFieldIsSet("cancel") {
return "redirect", urlStem return "redirect", urlStem
} }
@@ -247,7 +246,7 @@ func NewTopic(ctxt ui.AmContext) (string, any) {
if err != nil { if err != nil {
return "error", err return "error", err
} }
checker.SetContext("PostLinkDecoderContext", database.AmCreatePostLinkContext(comm.Alias, ctxt.URLParam("cid"), conf.TopTopic+1)) checker.SetContext("PostLinkDecoderContext", database.AmCreatePostLinkContext(comm.Alias, comm.Id, ctxt.GetScratch("currentAlias").(string), conf.TopTopic+1))
checker.Append(postdata) checker.Append(postdata)
checker.Finish() checker.Finish()
v, _ = checker.Value() v, _ = checker.Value()
@@ -282,7 +281,7 @@ func NewTopic(ctxt ui.AmContext) (string, any) {
if err != nil { if err != nil {
return "error", err return "error", err
} }
checker.SetContext("PostLinkDecoderContext", database.AmCreatePostLinkContext(comm.Alias, ctxt.URLParam("cid"), conf.TopTopic+1)) checker.SetContext("PostLinkDecoderContext", database.AmCreatePostLinkContext(comm.Alias, comm.Id, ctxt.GetScratch("currentAlias").(string), conf.TopTopic+1))
checker.Append(ctxt.FormField("pb")) checker.Append(ctxt.FormField("pb"))
checker.Finish() checker.Finish()
zeroPost, _ := checker.Value() zeroPost, _ := checker.Value()
@@ -385,7 +384,8 @@ func templatePostText(args jet.Arguments) reflect.Value {
// templateOverrideLine creates the "override line" for a post, that is, what gets displayed in place of the post text. // templateOverrideLine creates the "override line" for a post, that is, what gets displayed in place of the post text.
func templateOverrideLine(args jet.Arguments) reflect.Value { func templateOverrideLine(args jet.Arguments) reflect.Value {
post := args.Get(0).Convert(reflect.TypeFor[*database.PostHeader]()).Interface().(*database.PostHeader) post := args.Get(0).Convert(reflect.TypeFor[*database.PostHeader]()).Interface().(*database.PostHeader)
ctxt := args.Get(1).Convert(reflect.TypeFor[ui.AmContext]()).Interface().(ui.AmContext) advanced := args.Get(1).Convert(reflect.TypeFor[bool]()).Bool()
ctxt := args.Get(2).Convert(reflect.TypeFor[ui.AmContext]()).Interface().(ui.AmContext)
rc := "" rc := ""
if post.IsScribbled() { if post.IsScribbled() {
scr_date := "" scr_date := ""
@@ -402,7 +402,7 @@ func templateOverrideLine(args jet.Arguments) reflect.Value {
} else { } else {
rc = fmt.Sprintf("<<<%v>>>", err) rc = fmt.Sprintf("<<<%v>>>", err)
} }
} else if post.Hidden { } else if post.Hidden && !advanced {
rc = fmt.Sprintf("(Hidden Message: %d Lines)", *post.LineCount) rc = fmt.Sprintf("(Hidden Message: %d Lines)", *post.LineCount)
} }
return reflect.ValueOf(rc) return reflect.ValueOf(rc)
@@ -411,9 +411,10 @@ func templateOverrideLine(args jet.Arguments) reflect.Value {
// templateOverrideLink creates the "override link" for a post, which can make the override line a hyperlink. // templateOverrideLink creates the "override link" for a post, which can make the override line a hyperlink.
func templateOverrideLink(args jet.Arguments) reflect.Value { func templateOverrideLink(args jet.Arguments) reflect.Value {
post := args.Get(0).Convert(reflect.TypeFor[*database.PostHeader]()).Interface().(*database.PostHeader) post := args.Get(0).Convert(reflect.TypeFor[*database.PostHeader]()).Interface().(*database.PostHeader)
root := args.Get(1).Convert(reflect.TypeFor[string]()).String() advanced := args.Get(1).Convert(reflect.TypeFor[bool]()).Bool()
root := args.Get(2).Convert(reflect.TypeFor[string]()).String()
rc := "" rc := ""
if post.Hidden { if post.Hidden && !advanced {
rc = fmt.Sprintf("%s?r=%d&ac=1", root, post.Num) rc = fmt.Sprintf("%s?r=%d&ac=1", root, post.Num)
} }
return reflect.ValueOf(rc) return reflect.ValueOf(rc)
@@ -560,10 +561,11 @@ func ReadPosts(ctxt ui.AmContext) (string, any) {
summaryLine := fmt.Sprintf("%d Total; %d New; Last: %s", topic.TopMessage+1, topic.TopMessage-lastRead, prefs.Localizer().Strftime("%b %e, %Y %r", topic.LastUpdate)) summaryLine := fmt.Sprintf("%d Total; %d New; Last: %s", topic.TopMessage+1, topic.TopMessage-lastRead, prefs.Localizer().Strftime("%b %e, %Y %r", topic.LastUpdate))
ctxt.VarMap().Set("summaryLine", flags.String()+summaryLine) ctxt.VarMap().Set("summaryLine", flags.String()+summaryLine)
ctxt.SetFrameTitle(fmt.Sprintf("%s: %s%s", topic.Name, flags.String(), summaryLine)) ctxt.SetFrameTitle(fmt.Sprintf("%s: %s%s", topic.Name, flags.String(), summaryLine))
plc := database.AmCreatePostLinkContext("", ctxt.GetScratch("currentAlias").(string), topic.Number) plc := database.AmCreatePostLinkContext("", comm.Id, ctxt.GetScratch("currentAlias").(string), topic.Number)
ctxt.VarMap().Set("post_confRef", plc.AsString()) ctxt.VarMap().Set("post_confRef", plc.AsString())
plc.Community = comm.Alias plc.Community = comm.Alias
ctxt.VarMap().Set("post_topicPermalink", fmt.Sprintf("/go/%s", plc.AsString())) ctxt.VarMap().Set("post_topicPermalink", fmt.Sprintf("/go/%s", plc.AsString()))
ctxt.VarMap().Set("post_topicLink", fmt.Sprintf("%s/r/%d", ctxt.GetScratch("ConferenceLink"), topic.Number))
plc.FirstPost = postRange[0] plc.FirstPost = postRange[0]
plc.LastPost = postRange[1] plc.LastPost = postRange[1]
ctxt.VarMap().Set("postsPermalink", fmt.Sprintf("/go/%s", plc.AsString())) ctxt.VarMap().Set("postsPermalink", fmt.Sprintf("/go/%s", plc.AsString()))
@@ -631,7 +633,7 @@ func ReadPosts(ctxt ui.AmContext) (string, any) {
ctxt.VarMap().Set("advancedControls", advancedControls) ctxt.VarMap().Set("advancedControls", advancedControls)
// Adjust the traverser and get the "next" link. // Adjust the traverser and get the "next" link.
urlStem := fmt.Sprintf("/comm/%s/conf/%s", comm.Alias, ctxt.GetScratch("currentAlias").(string)) urlStem := ctxt.GetScratch("ConferenceLink").(string)
if traverser != nil { if traverser != nil {
traverser.ClearTopic(topic.Number) traverser.ClearTopic(topic.Number)
nextTopic := traverser.NextTopic(topic.Number) nextTopic := traverser.NextTopic(topic.Number)
@@ -686,7 +688,7 @@ func PostInTopic(ctxt ui.AmContext) (string, any) {
topic := ctxt.GetScratch("currentTopic").(*database.Topic) topic := ctxt.GetScratch("currentTopic").(*database.Topic)
ctxt.VarMap().Set("post_topic", topic) ctxt.VarMap().Set("post_topic", topic)
urlStem := fmt.Sprintf("/comm/%s/conf/%s/r/%d", comm.Alias, ctxt.GetScratch("currentAlias"), topic.Number) urlStem := fmt.Sprintf("%s/r/%d", ctxt.GetScratch("ConferenceLink"), topic.Number)
if ctxt.FormFieldIsSet("cancel") { if ctxt.FormFieldIsSet("cancel") {
return "redirect", urlStem return "redirect", urlStem
} }
@@ -734,7 +736,7 @@ func PostInTopic(ctxt ui.AmContext) (string, any) {
if err != nil { if err != nil {
return "error", err return "error", err
} }
checker.SetContext("PostLinkDecoderContext", database.AmCreatePostLinkContext(comm.Alias, ctxt.URLParam("cid"), topic.Number)) checker.SetContext("PostLinkDecoderContext", database.AmCreatePostLinkContext(comm.Alias, comm.Id, ctxt.GetScratch("currentAlias").(string), topic.Number))
checker.Append(postdata) checker.Append(postdata)
checker.Finish() checker.Finish()
v, _ = checker.Value() v, _ = checker.Value()
@@ -754,7 +756,7 @@ func PostInTopic(ctxt ui.AmContext) (string, any) {
} else if ctxt.FormFieldIsSet("postnext") && len(urlNextTopic) > 0 { } else if ctxt.FormFieldIsSet("postnext") && len(urlNextTopic) > 0 {
returnURL = urlNextTopic returnURL = urlNextTopic
} else if ctxt.FormFieldIsSet("posttopics") { } else if ctxt.FormFieldIsSet("posttopics") {
returnURL = fmt.Sprintf("/comm/%s/conf/%s", comm.Alias, ctxt.GetScratch("currentAlias")) returnURL = ctxt.GetScratch("ConferenceLink").(string)
} else { } else {
return "error", EBUTTON return "error", EBUTTON
} }
@@ -772,10 +774,13 @@ func PostInTopic(ctxt ui.AmContext) (string, any) {
return "error", err return "error", err
} }
plc := database.AmCreatePostLinkContext("", ctxt.GetScratch("currentAlias").(string), topic.Number) plc := database.AmCreatePostLinkContext("", comm.Id, ctxt.GetScratch("currentAlias").(string), topic.Number)
ctxt.VarMap().Set("post_confRef", plc.AsString()) ctxt.VarMap().Set("post_confRef", plc.AsString())
plc.Community = comm.Alias plc.Community = comm.Alias
ctxt.VarMap().Set("post_topicPermalink", fmt.Sprintf("/go/%s", plc.AsString())) ctxt.VarMap().Set("post_topicPermalink", fmt.Sprintf("/go/%s", plc.AsString()))
t := fmt.Sprintf("%s/r/%d", ctxt.GetScratch("ConferenceLink"), topic.Number)
ctxt.VarMap().Set("post_topicLink", t)
ctxt.VarMap().Set("post_stem", t)
ctxt.VarMap().SetFunc("post_getOverrideLine", templateOverrideLine) ctxt.VarMap().SetFunc("post_getOverrideLine", templateOverrideLine)
ctxt.VarMap().SetFunc("post_getOverrideLink", templateOverrideLink) ctxt.VarMap().SetFunc("post_getOverrideLink", templateOverrideLink)
@@ -783,7 +788,6 @@ func PostInTopic(ctxt ui.AmContext) (string, any) {
ctxt.VarMap().SetFunc("post_getUserName", templateExtractUserName) ctxt.VarMap().SetFunc("post_getUserName", templateExtractUserName)
ctxt.VarMap().SetFunc("post_getAttachmentInfo", templateAttachmentInfo) ctxt.VarMap().SetFunc("post_getAttachmentInfo", templateAttachmentInfo)
ctxt.VarMap().SetFunc("post_isBozo", templateBozo) ctxt.VarMap().SetFunc("post_isBozo", templateBozo)
ctxt.VarMap().Set("post_stem", fmt.Sprintf("/comm/%s/conf/%s/r/%d", comm.Alias, ctxt.GetScratch("currentAlias"), topic.Number))
ctxt.VarMap().Set("post_max", topic.TopMessage) ctxt.VarMap().Set("post_max", topic.TopMessage)
ctxt.VarMap().Set("posts", posts) ctxt.VarMap().Set("posts", posts)
ctxt.VarMap().Set("topicName", topic.Name) ctxt.VarMap().Set("topicName", topic.Name)
@@ -805,7 +809,8 @@ func PostInTopic(ctxt ui.AmContext) (string, any) {
if err != nil { if err != nil {
return "error", err return "error", err
} }
checker.SetContext("PostLinkDecoderContext", database.AmCreatePostLinkContext(comm.Alias, ctxt.URLParam("cid"), topic.Number)) checker.SetContext("PostLinkDecoderContext", database.AmCreatePostLinkContext(comm.Alias, comm.Id,
ctxt.GetScratch("currentAlias").(string), topic.Number))
checker.Append(ctxt.FormField("pb")) checker.Append(ctxt.FormField("pb"))
checker.Finish() checker.Finish()
postText, _ := checker.Value() postText, _ := checker.Value()
+29 -33
View File
@@ -156,7 +156,7 @@ func ConfManage(ctxt ui.AmContext) (string, any) {
comm := ctxt.CurrentCommunity() comm := ctxt.CurrentCommunity()
conf := ctxt.GetScratch("currentConference").(*database.Conference) conf := ctxt.GetScratch("currentConference").(*database.Conference)
myLevel := ctxt.GetScratch("levelInConference").(uint16) myLevel := ctxt.GetScratch("levelInConference").(uint16)
urlStem := fmt.Sprintf("/comm/%s/conf/%s", comm.Alias, ctxt.GetScratch("currentAlias")) urlStem := ctxt.GetScratch("ConferenceLink").(string)
ctxt.VarMap().Set("confName", conf.Name) ctxt.VarMap().Set("confName", conf.Name)
ctxt.VarMap().Set("urlStem", urlStem) ctxt.VarMap().Set("urlStem", urlStem)
@@ -193,14 +193,13 @@ func ConfManage(ctxt ui.AmContext) (string, any) {
* Data as a parameter for the command string. * Data as a parameter for the command string.
*/ */
func SetPseud(ctxt ui.AmContext) (string, any) { func SetPseud(ctxt ui.AmContext) (string, any) {
comm := ctxt.CurrentCommunity()
conf := ctxt.GetScratch("currentConference").(*database.Conference) conf := ctxt.GetScratch("currentConference").(*database.Conference)
pseud := ctxt.FormField("pseud") pseud := ctxt.FormField("pseud")
err := conf.SetDefaultPseud(ctxt.Ctx(), ctxt.CurrentUser(), pseud) err := conf.SetDefaultPseud(ctxt.Ctx(), ctxt.CurrentUser(), pseud)
if err != nil { if err != nil {
return "error", err return "error", err
} }
return "redirect", fmt.Sprintf("/comm/%s/conf/%s/manage", comm.Alias, ctxt.GetScratch("currentAlias")) return "redirect", fmt.Sprintf("%s/manage", ctxt.GetScratch("ConferenceLink"))
} }
/* ConfFixseen marks all messages in a conference as read. /* ConfFixseen marks all messages in a conference as read.
@@ -211,13 +210,12 @@ func SetPseud(ctxt ui.AmContext) (string, any) {
* Data as a parameter for the command string. * Data as a parameter for the command string.
*/ */
func ConfFixseen(ctxt ui.AmContext) (string, any) { func ConfFixseen(ctxt ui.AmContext) (string, any) {
comm := ctxt.CurrentCommunity()
conf := ctxt.GetScratch("currentConference").(*database.Conference) conf := ctxt.GetScratch("currentConference").(*database.Conference)
err := conf.Fixseen(ctxt.Ctx(), ctxt.CurrentUser()) err := conf.Fixseen(ctxt.Ctx(), ctxt.CurrentUser())
if err != nil { if err != nil {
return "error", err return "error", err
} }
return "redirect", fmt.Sprintf("/comm/%s/conf/%s/manage", comm.Alias, ctxt.GetScratch("currentAlias")) return "redirect", fmt.Sprintf("%s/manage", ctxt.GetScratch("ConferenceLink"))
} }
/* AddToHotlist adds the current community and conference to the user's hotlist.. /* AddToHotlist adds the current community and conference to the user's hotlist..
@@ -234,7 +232,7 @@ func AddToHotlist(ctxt ui.AmContext) (string, any) {
if err != nil { if err != nil {
return "error", err return "error", err
} }
return "redirect", fmt.Sprintf("/comm/%s/conf/%s", comm.Alias, ctxt.GetScratch("currentAlias")) return "redirect", ctxt.GetScratch("ConferenceLink").(string)
} }
/* HideTopic hides or shows the current topic for the current user. /* HideTopic hides or shows the current topic for the current user.
@@ -254,7 +252,7 @@ func HideTopic(ctxt ui.AmContext) (string, any) {
if err != nil { if err != nil {
return "error", err return "error", err
} }
return "redirect", fmt.Sprintf("/comm/%s/conf/%s/r/%d", ctxt.CurrentCommunity().Alias, ctxt.GetScratch("currentAlias"), topic.Number) return "redirect", fmt.Sprintf("%s/r/%d", ctxt.GetScratch("ConferenceLink"), topic.Number)
} }
/* FreezeTopic freezes or unfreezes the current topic. /* FreezeTopic freezes or unfreezes the current topic.
@@ -275,7 +273,7 @@ func FreezeTopic(ctxt ui.AmContext) (string, any) {
if err != nil { if err != nil {
return "error", err return "error", err
} }
return "redirect", fmt.Sprintf("/comm/%s/conf/%s/r/%d", ctxt.CurrentCommunity().Alias, ctxt.GetScratch("currentAlias"), topic.Number) return "redirect", fmt.Sprintf("%s/r/%d", ctxt.GetScratch("ConferenceLink"), topic.Number)
} }
/* ArchiveTopic archives or unarchives the current topic. /* ArchiveTopic archives or unarchives the current topic.
@@ -296,7 +294,7 @@ func ArchiveTopic(ctxt ui.AmContext) (string, any) {
if err != nil { if err != nil {
return "error", err return "error", err
} }
return "redirect", fmt.Sprintf("/comm/%s/conf/%s/r/%d", ctxt.CurrentCommunity().Alias, ctxt.GetScratch("currentAlias"), topic.Number) return "redirect", fmt.Sprintf("%s/r/%d", ctxt.GetScratch("ConferenceLink"), topic.Number)
} }
/* StickTopic sticks or unsticks the current topic. /* StickTopic sticks or unsticks the current topic.
@@ -317,7 +315,7 @@ func StickTopic(ctxt ui.AmContext) (string, any) {
if err != nil { if err != nil {
return "error", err return "error", err
} }
return "redirect", fmt.Sprintf("/comm/%s/conf/%s/r/%d", ctxt.CurrentCommunity().Alias, ctxt.GetScratch("currentAlias"), topic.Number) return "redirect", fmt.Sprintf("%s/r/%d", ctxt.GetScratch("ConferenceLink"), topic.Number)
} }
/* DeleteTopic deletes the current topic. /* DeleteTopic deletes the current topic.
@@ -348,14 +346,14 @@ func DeleteTopic(ctxt ui.AmContext) (string, any) {
if err != nil { if err != nil {
return "error", err return "error", err
} }
return "redirect", fmt.Sprintf("/comm/%s/conf/%s", ctxt.CurrentCommunity().Alias, ctxt.GetScratch("currentAlias")) return "redirect", ctxt.GetScratch("ConferenceLink").(string)
} }
// Set up to display the message box. // Set up to display the message box.
mbox.SetMessage(fmt.Sprintf(`You are about to detele the topic <span class="font-bold text-red-600">"%s"</span> mbox.SetMessage(fmt.Sprintf(`You are about to detele the topic <span class="font-bold text-red-600">"%s"</span>
from the <span class="font-bold text-red-600">"%s"</span> conference!`, topic.Name, conf.Name)) from the <span class="font-bold text-red-600">"%s"</span> conference!`, topic.Name, conf.Name))
mbox.SetLink("no", fmt.Sprintf("/comm/%s/conf/%s/r/%d", ctxt.CurrentCommunity().Alias, ctxt.GetScratch("currentAlias"), topic.Number)) mbox.SetLink("no", fmt.Sprintf("%s/r/%d", ctxt.GetScratch("ConferenceLink"), topic.Number))
mbox.SetLink("yes", fmt.Sprintf("/comm/%s/conf/%s/op/%d/delete", ctxt.CurrentCommunity().Alias, ctxt.GetScratch("currentAlias"), topic.Number)) mbox.SetLink("yes", fmt.Sprintf("%s/op/%d/delete", ctxt.GetScratch("ConferenceLink"), topic.Number))
return mbox.Render(ctxt) return mbox.Render(ctxt)
} }
@@ -390,7 +388,7 @@ func HideMessage(ctxt ui.AmContext) (string, any) {
if err != nil { if err != nil {
return "error", err return "error", err
} }
return "redirect", fmt.Sprintf("/comm/%s/conf/%s/r/%d?r=%d&ac=1", ctxt.CurrentCommunity().Alias, ctxt.GetScratch("currentAlias"), topic.Number, hdrs[0].Num) return "redirect", fmt.Sprintf("%s/r/%d?r=%d&ac=1", ctxt.GetScratch("ConferenceLink"), topic.Number, hdrs[0].Num)
} }
/* ScribbleMessage scribbles a topic message. /* ScribbleMessage scribbles a topic message.
@@ -424,7 +422,7 @@ func ScribbleMessage(ctxt ui.AmContext) (string, any) {
if err != nil { if err != nil {
return "error", err return "error", err
} }
return "redirect", fmt.Sprintf("/comm/%s/conf/%s/r/%d?r=%d&ac=1", ctxt.CurrentCommunity().Alias, ctxt.GetScratch("currentAlias"), topic.Number, hdrs[0].Num) return "redirect", fmt.Sprintf("%s/r/%d?r=%d&ac=1", ctxt.GetScratch("ConferenceLink"), topic.Number, hdrs[0].Num)
} }
/* NukeMessage nukes (deletes entirely) a topic message. /* NukeMessage nukes (deletes entirely) a topic message.
@@ -466,11 +464,11 @@ func NukeMessage(ctxt ui.AmContext) (string, any) {
if err != nil { if err != nil {
return "error", err return "error", err
} }
return "redirect", fmt.Sprintf("/comm/%s/conf/%s/r/%d", ctxt.CurrentCommunity().Alias, ctxt.GetScratch("currentAlias"), topic.Number) return "redirect", fmt.Sprintf("%s/r/%d", ctxt.GetScratch("ConferenceLink"), topic.Number)
} }
// Set up to display the message box. // Set up to display the message box.
link, err := hdrs[0].Link(ctxt.Ctx(), "community") link, err := hdrs[0].Link(ctxt.Ctx(), ctxt.CurrentCommunity().Id, "community")
if err != nil { if err != nil {
return "error", err return "error", err
} }
@@ -480,8 +478,8 @@ func NukeMessage(ctxt ui.AmContext) (string, any) {
} }
mbox.SetMessage(fmt.Sprintf(`You are about to nuke message <span class="font-mono font-bold text-red-600">&lt;%s&gt;</span>, mbox.SetMessage(fmt.Sprintf(`You are about to nuke message <span class="font-mono font-bold text-red-600">&lt;%s&gt;</span>,
originally composed by <span class="font-bold text-red-600">&lt;%s&gt;</span>!`, link, creator.Username)) originally composed by <span class="font-bold text-red-600">&lt;%s&gt;</span>!`, link, creator.Username))
mbox.SetLink("no", fmt.Sprintf("/comm/%s/conf/%s/r/%d?r=%d&ac=1", ctxt.CurrentCommunity().Alias, ctxt.GetScratch("currentAlias"), topic.Number, hdrs[0].Num)) mbox.SetLink("no", fmt.Sprintf("%s/r/%d?r=%d&ac=1", ctxt.GetScratch("ConferenceLink"), topic.Number, hdrs[0].Num))
mbox.SetLink("yes", fmt.Sprintf("/comm/%s/conf/%s/op/%d/nuke/%d", ctxt.CurrentCommunity().Alias, ctxt.GetScratch("currentAlias"), topic.Number, hdrs[0].Num)) mbox.SetLink("yes", fmt.Sprintf("%s/op/%d/nuke/%d", ctxt.GetScratch("ConferenceLink"), topic.Number, hdrs[0].Num))
return mbox.Render(ctxt) return mbox.Render(ctxt)
} }
@@ -524,11 +522,11 @@ func PruneMessageAttachment(ctxt ui.AmContext) (string, any) {
if err != nil { if err != nil {
return "error", err return "error", err
} }
return "redirect", fmt.Sprintf("/comm/%s/conf/%s/r/%d?r=%d&ac=1", ctxt.CurrentCommunity().Alias, ctxt.GetScratch("currentAlias"), topic.Number, hdrs[0].Num) return "redirect", fmt.Sprintf("%s/r/%d?r=%d&ac=1", ctxt.GetScratch("ConferenceLink"), topic.Number, hdrs[0].Num)
} }
// Set up to display the message box. // Set up to display the message box.
link, err := hdrs[0].Link(ctxt.Ctx(), "community") link, err := hdrs[0].Link(ctxt.Ctx(), ctxt.CurrentCommunity().Id, "community")
if err != nil { if err != nil {
return "error", err return "error", err
} }
@@ -538,8 +536,8 @@ func PruneMessageAttachment(ctxt ui.AmContext) (string, any) {
} }
mbox.SetMessage(fmt.Sprintf(`You are about to prune the attachment of message <span class="font-mono font-bold text-red-600">&lt;%s&gt;</span>, mbox.SetMessage(fmt.Sprintf(`You are about to prune the attachment of message <span class="font-mono font-bold text-red-600">&lt;%s&gt;</span>,
originally composed by <span class="font-bold text-red-600">&lt;%s&gt;</span>!`, link, creator.Username)) originally composed by <span class="font-bold text-red-600">&lt;%s&gt;</span>!`, link, creator.Username))
mbox.SetLink("no", fmt.Sprintf("/comm/%s/conf/%s/r/%d?r=%d&ac=1", ctxt.CurrentCommunity().Alias, ctxt.GetScratch("currentAlias"), topic.Number, hdrs[0].Num)) mbox.SetLink("no", fmt.Sprintf("%s/r/%d?r=%d&ac=1", ctxt.GetScratch("ConferenceLink"), topic.Number, hdrs[0].Num))
mbox.SetLink("yes", fmt.Sprintf("/comm/%s/conf/%s/op/%d/prune/%d", ctxt.CurrentCommunity().Alias, ctxt.GetScratch("currentAlias"), topic.Number, hdrs[0].Num)) mbox.SetLink("yes", fmt.Sprintf("%s/op/%d/prune/%d", ctxt.GetScratch("ConferenceLink"), topic.Number, hdrs[0].Num))
return mbox.Render(ctxt) return mbox.Render(ctxt)
} }
@@ -591,7 +589,7 @@ func MoveMessageForm(ctxt ui.AmContext) (string, any) {
ctxt.VarMap().Set("post", hdrs[0]) ctxt.VarMap().Set("post", hdrs[0])
ctxt.VarMap().Set("topMessage", topic.TopMessage) ctxt.VarMap().Set("topMessage", topic.TopMessage)
formLink := fmt.Sprintf("/comm/%s/conf/%s/op/%d/move/%d", ctxt.CurrentCommunity().Alias, ctxt.GetScratch("currentAlias"), topic.Number, hdrs[0].Num) formLink := fmt.Sprintf("%s/op/%d/move/%d", ctxt.GetScratch("ConferenceLink"), topic.Number, hdrs[0].Num)
ctxt.VarMap().Set("formLink", formLink) ctxt.VarMap().Set("formLink", formLink)
ctxt.SetFrameTitle("Move Message") ctxt.SetFrameTitle("Move Message")
@@ -625,7 +623,7 @@ func PublishMessage(ctxt ui.AmContext) (string, any) {
if err = hdrs[0].Publish(ctxt.Ctx(), comm, ctxt.CurrentUser(), ctxt.RemoteIP()); err != nil { if err = hdrs[0].Publish(ctxt.Ctx(), comm, ctxt.CurrentUser(), ctxt.RemoteIP()); err != nil {
return "error", err return "error", err
} }
return "redirect", fmt.Sprintf("/comm/%s/conf/%s/r/%d?r=%d&ac=1", ctxt.CurrentCommunity().Alias, ctxt.GetScratch("currentAlias"), topic.Number, hdrs[0].Num) return "redirect", fmt.Sprintf("%s/r/%d?r=%d&ac=1", ctxt.GetScratch("ConferenceLink"), topic.Number, hdrs[0].Num)
} }
/* MoveMessage moves a message to a different topic. /* MoveMessage moves a message to a different topic.
@@ -654,7 +652,7 @@ func MoveMessage(ctxt ui.AmContext) (string, any) {
return "error", EPOSTREF return "error", EPOSTREF
} }
if ctxt.FormFieldIsSet("cancel") { if ctxt.FormFieldIsSet("cancel") {
return "redirect", fmt.Sprintf("/comm/%s/conf/%s/r/%d?r=%d&ac=1", comm.Alias, ctxt.GetScratch("currentAlias"), topic.Number, hdrs[0].Num) return "redirect", fmt.Sprintf("%s/r/%d?r=%d&ac=1", ctxt.GetScratch("ConferenceLink"), topic.Number, hdrs[0].Num)
} }
if !conf.TestPermission("Conference.Nuke", myLevel) || !conf.TestPermission("Conference.Post", myLevel) || topic.TopMessage == 0 { if !conf.TestPermission("Conference.Nuke", myLevel) || !conf.TestPermission("Conference.Post", myLevel) || topic.TopMessage == 0 {
return "error", ENOPERM return "error", ENOPERM
@@ -696,7 +694,7 @@ func MoveMessage(ctxt ui.AmContext) (string, any) {
}) })
} }
return "redirect", fmt.Sprintf("/comm/%s/conf/%s/r/%d", ctxt.CurrentCommunity().Alias, ctxt.GetScratch("currentAlias"), topic.Number) return "redirect", fmt.Sprintf("%s/r/%d", ctxt.GetScratch("ConferenceLink"), topic.Number)
} }
/* TopicManage displays the "manage topic" page. /* TopicManage displays the "manage topic" page.
@@ -709,8 +707,8 @@ func MoveMessage(ctxt ui.AmContext) (string, any) {
func TopicManage(ctxt ui.AmContext) (string, any) { func TopicManage(ctxt ui.AmContext) (string, any) {
comm := ctxt.CurrentCommunity() comm := ctxt.CurrentCommunity()
topic := ctxt.GetScratch("currentTopic").(*database.Topic) topic := ctxt.GetScratch("currentTopic").(*database.Topic)
ctxt.VarMap().Set("backlink", fmt.Sprintf("/comm/%s/conf/%s/r/%d", comm.Alias, ctxt.GetScratch("currentAlias"), topic.Number)) ctxt.VarMap().Set("backlink", fmt.Sprintf("%s/r/%d", ctxt.GetScratch("ConferenceLink"), topic.Number))
opsLink := fmt.Sprintf("/comm/%s/conf/%s/op/%d", comm.Alias, ctxt.GetScratch("currentAlias"), topic.Number) opsLink := fmt.Sprintf("%s/op/%d", ctxt.GetScratch("ConferenceLink"), topic.Number)
ctxt.VarMap().Set("opsLink", opsLink) ctxt.VarMap().Set("opsLink", opsLink)
ctxt.VarMap().Set("topicName", topic.Name) ctxt.VarMap().Set("topicName", topic.Name)
@@ -750,7 +748,6 @@ func TopicSetSubscribe(ctxt ui.AmContext) (string, any) {
if ctxt.CurrentUser().IsAnon { if ctxt.CurrentUser().IsAnon {
return "error", ENOPERM return "error", ENOPERM
} }
comm := ctxt.CurrentCommunity()
topic := ctxt.GetScratch("currentTopic").(*database.Topic) topic := ctxt.GetScratch("currentTopic").(*database.Topic)
flag, err := topic.IsSubscribed(ctxt.Ctx(), ctxt.CurrentUser()) flag, err := topic.IsSubscribed(ctxt.Ctx(), ctxt.CurrentUser())
if err != nil { if err != nil {
@@ -760,7 +757,7 @@ func TopicSetSubscribe(ctxt ui.AmContext) (string, any) {
if err != nil { if err != nil {
return "error", err return "error", err
} }
return "redirect", fmt.Sprintf("/comm/%s/conf/%s/op/%d/manage", comm.Alias, ctxt.GetScratch("currentAlias"), topic.Number) return "redirect", fmt.Sprintf("%s/op/%d/manage", ctxt.GetScratch("ConferenceLink"), topic.Number)
} }
/* TopicRemoveBozo removes filtering from a specified user in the topic. /* TopicRemoveBozo removes filtering from a specified user in the topic.
@@ -771,7 +768,6 @@ func TopicSetSubscribe(ctxt ui.AmContext) (string, any) {
* Data as a parameter for the command string. * Data as a parameter for the command string.
*/ */
func TopicRemoveBozo(ctxt ui.AmContext) (string, any) { func TopicRemoveBozo(ctxt ui.AmContext) (string, any) {
comm := ctxt.CurrentCommunity()
topic := ctxt.GetScratch("currentTopic").(*database.Topic) topic := ctxt.GetScratch("currentTopic").(*database.Topic)
bozoUid, err := strconv.Atoi(ctxt.URLParam("uid")) bozoUid, err := strconv.Atoi(ctxt.URLParam("uid"))
if err != nil { if err != nil {
@@ -781,5 +777,5 @@ func TopicRemoveBozo(ctxt ui.AmContext) (string, any) {
if err != nil { if err != nil {
return "error", err return "error", err
} }
return "redirect", fmt.Sprintf("/comm/%s/conf/%s/op/%d/manage", comm.Alias, ctxt.GetScratch("currentAlias"), topic.Number) return "redirect", fmt.Sprintf("%s/op/%d/manage", ctxt.GetScratch("ConferenceLink"), topic.Number)
} }
+34 -46
View File
@@ -99,7 +99,7 @@ func EditConference(ctxt ui.AmContext) (string, any) {
} }
button := dlg.WhichButton(ctxt) button := dlg.WhichButton(ctxt)
if button == "cancel" { if button == "cancel" {
return "redirect", fmt.Sprintf("/comm/%s/conf/%s/manage", comm.Alias, ctxt.GetScratch("currentAlias")) return "redirect", fmt.Sprintf("%s/manage", ctxt.GetScratch("ConferenceLink"))
} else if button != "update" { } else if button != "update" {
dlg.SetCommunity(comm) dlg.SetCommunity(comm)
dlg.SetConference(conf, ctxt.GetScratch("currentAlias").(string)) dlg.SetConference(conf, ctxt.GetScratch("currentAlias").(string))
@@ -128,7 +128,7 @@ func EditConference(ctxt ui.AmContext) (string, any) {
return dlg.RenderError(ctxt, err.Error()) return dlg.RenderError(ctxt, err.Error())
} }
return "redirect", fmt.Sprintf("/comm/%s/conf/%s/manage", comm.Alias, ctxt.GetScratch("currentAlias")) return "redirect", fmt.Sprintf("%s/manage", ctxt.GetScratch("ConferenceLink"))
} }
/* ConferenceAliasForm displays the form for managing conference aliases. /* ConferenceAliasForm displays the form for managing conference aliases.
@@ -148,8 +148,8 @@ func ConferenceAliasForm(ctxt ui.AmContext) (string, any) {
ctxt.VarMap().Set("newAlias", "") ctxt.VarMap().Set("newAlias", "")
ctxt.VarMap().Set("confName", conf.Name) ctxt.VarMap().Set("confName", conf.Name)
ctxt.VarMap().Set("backLink", fmt.Sprintf("/comm/%s/conf/%s/manage", comm.Alias, ctxt.GetScratch("currentAlias"))) ctxt.VarMap().Set("backLink", fmt.Sprintf("%s/manage", ctxt.GetScratch("ConferenceLink")))
ctxt.VarMap().Set("selfLink", fmt.Sprintf("/comm/%s/conf/%s/aliases", comm.Alias, ctxt.GetScratch("currentAlias"))) ctxt.VarMap().Set("selfLink", fmt.Sprintf("%s/aliases", ctxt.GetScratch("ConferenceLink")))
ctxt.SetFrameTitle(fmt.Sprintf("Manage Conference Aliases: %s", conf.Name)) ctxt.SetFrameTitle(fmt.Sprintf("Manage Conference Aliases: %s", conf.Name))
if ctxt.HasParameter("del") { if ctxt.HasParameter("del") {
@@ -159,7 +159,7 @@ func ConferenceAliasForm(ctxt ui.AmContext) (string, any) {
} }
} }
aliases, err := conf.Aliases(ctxt.Ctx()) aliases, err := conf.Aliases(ctxt.Ctx(), comm.Id)
if err != nil { if err != nil {
return "error", err return "error", err
} }
@@ -184,8 +184,8 @@ func ConferenceAliasAdd(ctxt ui.AmContext) (string, any) {
} }
ctxt.VarMap().Set("confName", conf.Name) ctxt.VarMap().Set("confName", conf.Name)
ctxt.VarMap().Set("backLink", fmt.Sprintf("/comm/%s/conf/%s/manage", comm.Alias, ctxt.GetScratch("currentAlias"))) ctxt.VarMap().Set("backLink", fmt.Sprintf("%s/manage", ctxt.GetScratch("ConferenceLink")))
ctxt.VarMap().Set("selfLink", fmt.Sprintf("/comm/%s/conf/%s/aliases", comm.Alias, ctxt.GetScratch("currentAlias"))) ctxt.VarMap().Set("selfLink", fmt.Sprintf("%s/aliases", ctxt.GetScratch("ConferenceLink")))
ctxt.SetFrameTitle(fmt.Sprintf("Manage Conference Aliases: %s", conf.Name)) ctxt.SetFrameTitle(fmt.Sprintf("Manage Conference Aliases: %s", conf.Name))
newAlias := ctxt.FormField("na") newAlias := ctxt.FormField("na")
@@ -206,7 +206,7 @@ func ConferenceAliasAdd(ctxt ui.AmContext) (string, any) {
ctxt.VarMap().Set("errorMessage", err.Error()) ctxt.VarMap().Set("errorMessage", err.Error())
} }
aliases, err := conf.Aliases(ctxt.Ctx()) aliases, err := conf.Aliases(ctxt.Ctx(), comm.Id)
if err != nil { if err != nil {
return "error", err return "error", err
} }
@@ -241,8 +241,8 @@ func ConferenceMembers(ctxt ui.AmContext) (string, any) {
// Set the first batch of page variables. // Set the first batch of page variables.
ctxt.VarMap().Set("commName", comm.Name) ctxt.VarMap().Set("commName", comm.Name)
ctxt.VarMap().Set("confName", conf.Name) ctxt.VarMap().Set("confName", conf.Name)
ctxt.VarMap().Set("backLink", fmt.Sprintf("/comm/%s/conf/%s/manage", comm.Alias, ctxt.GetScratch("currentAlias"))) ctxt.VarMap().Set("backLink", fmt.Sprintf("%s/manage", ctxt.GetScratch("ConferenceLink")))
ctxt.VarMap().Set("selfLink", fmt.Sprintf("/comm/%s/conf/%s/members", comm.Alias, ctxt.GetScratch("currentAlias"))) ctxt.VarMap().Set("selfLink", fmt.Sprintf("%s/members", ctxt.GetScratch("ConferenceLink")))
ctxt.VarMap().Set("roleList", database.AmRoleList("Conference.UserLevels")) ctxt.VarMap().Set("roleList", database.AmRoleList("Conference.UserLevels"))
ctxt.SetFrameTitle(fmt.Sprintf("Membership in Conference: %s", conf.Name)) ctxt.SetFrameTitle(fmt.Sprintf("Membership in Conference: %s", conf.Name))
@@ -381,7 +381,6 @@ func ConferenceMembers(ctxt ui.AmContext) (string, any) {
* Data as a parameter for the command string. * Data as a parameter for the command string.
*/ */
func ConfCustomForm(ctxt ui.AmContext) (string, any) { func ConfCustomForm(ctxt ui.AmContext) (string, any) {
comm := ctxt.CurrentCommunity()
conf := ctxt.GetScratch("currentConference").(*database.Conference) conf := ctxt.GetScratch("currentConference").(*database.Conference)
myLevel := ctxt.GetScratch("levelInConference").(uint16) myLevel := ctxt.GetScratch("levelInConference").(uint16)
if !conf.TestPermission("Conference.Change", myLevel) { if !conf.TestPermission("Conference.Change", myLevel) {
@@ -394,7 +393,7 @@ func ConfCustomForm(ctxt ui.AmContext) (string, any) {
} }
ctxt.VarMap().Set("confName", conf.Name) ctxt.VarMap().Set("confName", conf.Name)
ctxt.VarMap().Set("selfLink", fmt.Sprintf("/comm/%s/conf/%s/custom", comm.Alias, ctxt.GetScratch("currentAlias"))) ctxt.VarMap().Set("selfLink", fmt.Sprintf("%s/custom", ctxt.GetScratch("ConferenceLink")))
ctxt.VarMap().Set("topText", topBlock) ctxt.VarMap().Set("topText", topBlock)
ctxt.VarMap().Set("bottomText", bottomBlock) ctxt.VarMap().Set("bottomText", bottomBlock)
ctxt.SetFrameTitle(fmt.Sprintf("Customize Conference: %s", conf.Name)) ctxt.SetFrameTitle(fmt.Sprintf("Customize Conference: %s", conf.Name))
@@ -409,7 +408,6 @@ func ConfCustomForm(ctxt ui.AmContext) (string, any) {
* Data as a parameter for the command string. * Data as a parameter for the command string.
*/ */
func ConfCustom(ctxt ui.AmContext) (string, any) { func ConfCustom(ctxt ui.AmContext) (string, any) {
comm := ctxt.CurrentCommunity()
conf := ctxt.GetScratch("currentConference").(*database.Conference) conf := ctxt.GetScratch("currentConference").(*database.Conference)
myLevel := ctxt.GetScratch("levelInConference").(uint16) myLevel := ctxt.GetScratch("levelInConference").(uint16)
if !conf.TestPermission("Conference.Change", myLevel) { if !conf.TestPermission("Conference.Change", myLevel) {
@@ -429,7 +427,7 @@ func ConfCustom(ctxt ui.AmContext) (string, any) {
if err != nil { if err != nil {
return "error", err return "error", err
} }
return "redirect", fmt.Sprintf("/comm/%s/conf/%s/manage", comm.Alias, ctxt.GetScratch("currentAlias")) return "redirect", fmt.Sprintf("%s/manage", ctxt.GetScratch("ConferenceLink"))
} }
/* ConfReports displays conference activity reports. /* ConfReports displays conference activity reports.
@@ -440,7 +438,6 @@ func ConfCustom(ctxt ui.AmContext) (string, any) {
* Data as a parameter for the command string. * Data as a parameter for the command string.
*/ */
func ConfReports(ctxt ui.AmContext) (string, any) { func ConfReports(ctxt ui.AmContext) (string, any) {
comm := ctxt.CurrentCommunity()
conf := ctxt.GetScratch("currentConference").(*database.Conference) conf := ctxt.GetScratch("currentConference").(*database.Conference)
myLevel := ctxt.GetScratch("levelInConference").(uint16) myLevel := ctxt.GetScratch("levelInConference").(uint16)
if !conf.TestPermission("Conference.Read", myLevel) { if !conf.TestPermission("Conference.Read", myLevel) {
@@ -448,7 +445,7 @@ func ConfReports(ctxt ui.AmContext) (string, any) {
} }
ctxt.VarMap().Set("confName", conf.Name) ctxt.VarMap().Set("confName", conf.Name)
ctxt.VarMap().Set("selfLink", fmt.Sprintf("/comm/%s/conf/%s/activity", comm.Alias, ctxt.GetScratch("currentAlias"))) ctxt.VarMap().Set("selfLink", fmt.Sprintf("%s/activity", ctxt.GetScratch("ConferenceLink")))
if ctxt.HasParameter("r") { if ctxt.HasParameter("r") {
// generate a report // generate a report
@@ -504,7 +501,7 @@ func ConfReports(ctxt ui.AmContext) (string, any) {
return "error", err return "error", err
} }
ctxt.VarMap().Set("topics", topicList) ctxt.VarMap().Set("topics", topicList)
ctxt.VarMap().Set("backLink", fmt.Sprintf("/comm/%s/conf/%s/manage", comm.Alias, ctxt.GetScratch("currentAlias"))) ctxt.VarMap().Set("backLink", fmt.Sprintf("%s/manage", ctxt.GetScratch("ConferenceLink")))
ctxt.SetFrameTitle(fmt.Sprintf("Conference Reports: %s", conf.Name)) ctxt.SetFrameTitle(fmt.Sprintf("Conference Reports: %s", conf.Name))
return "framed", "conf_reports.jet" return "framed", "conf_reports.jet"
} }
@@ -518,7 +515,6 @@ func ConfReports(ctxt ui.AmContext) (string, any) {
* Data as a parameter for the command string. * Data as a parameter for the command string.
*/ */
func ConferenceEmailForm(ctxt ui.AmContext) (string, any) { func ConferenceEmailForm(ctxt ui.AmContext) (string, any) {
comm := ctxt.CurrentCommunity()
conf := ctxt.GetScratch("currentConference").(*database.Conference) conf := ctxt.GetScratch("currentConference").(*database.Conference)
myLevel := ctxt.GetScratch("levelInConference").(uint16) myLevel := ctxt.GetScratch("levelInConference").(uint16)
if !conf.TestPermission("Conference.EMailParticipants", myLevel) { if !conf.TestPermission("Conference.EMailParticipants", myLevel) {
@@ -531,7 +527,7 @@ func ConferenceEmailForm(ctxt ui.AmContext) (string, any) {
} }
ctxt.VarMap().Set("topics", topics) ctxt.VarMap().Set("topics", topics)
ctxt.VarMap().Set("confName", conf.Name) ctxt.VarMap().Set("confName", conf.Name)
ctxt.VarMap().Set("selfLink", fmt.Sprintf("/comm/%s/conf/%s/email", comm.Alias, ctxt.GetScratch("currentAlias"))) ctxt.VarMap().Set("selfLink", fmt.Sprintf("%s/email", ctxt.GetScratch("ConferenceLink")))
ctxt.VarMap().Set("porl", 0).Set("top", 0).Set("xday", false) ctxt.VarMap().Set("porl", 0).Set("top", 0).Set("xday", false)
ctxt.VarMap().Set("day", 7).Set("subj", "").Set("pb", "") ctxt.VarMap().Set("day", 7).Set("subj", "").Set("pb", "")
ctxt.SetFrameTitle(fmt.Sprintf("Conference E-Mail: %s", conf.Name)) ctxt.SetFrameTitle(fmt.Sprintf("Conference E-Mail: %s", conf.Name))
@@ -555,7 +551,7 @@ func ConferenceEmail(ctxt ui.AmContext) (string, any) {
// Handle button presses. // Handle button presses.
if ctxt.FormFieldIsSet("cancel") { if ctxt.FormFieldIsSet("cancel") {
return "redirect", fmt.Sprintf("/comm/%s/conf/%s/manage", comm.Alias, ctxt.GetScratch("currentAlias")) return "redirect", fmt.Sprintf("%s/manage", ctxt.GetScratch("ConferenceLink"))
} else if !ctxt.FormFieldIsSet("send") { } else if !ctxt.FormFieldIsSet("send") {
return "error", EBUTTON return "error", EBUTTON
} }
@@ -646,7 +642,7 @@ func ConferenceEmail(ctxt ui.AmContext) (string, any) {
log.Infof("ConferenceEmail delivery completed in %s", elapsed) log.Infof("ConferenceEmail delivery completed in %s", elapsed)
}) })
return "redirect", fmt.Sprintf("/comm/%s/conf/%s/manage", comm.Alias, ctxt.GetScratch("currentAlias")) return "redirect", fmt.Sprintf("%s/manage", ctxt.GetScratch("ConferenceLink"))
} }
/* ConferenceExportForm displays the form for exporting data from a conference. /* ConferenceExportForm displays the form for exporting data from a conference.
@@ -657,7 +653,6 @@ func ConferenceEmail(ctxt ui.AmContext) (string, any) {
* Data as a parameter for the command string. * Data as a parameter for the command string.
*/ */
func ConferenceExportForm(ctxt ui.AmContext) (string, any) { func ConferenceExportForm(ctxt ui.AmContext) (string, any) {
comm := ctxt.CurrentCommunity()
conf := ctxt.GetScratch("currentConference").(*database.Conference) conf := ctxt.GetScratch("currentConference").(*database.Conference)
myLevel := ctxt.GetScratch("levelInConference").(uint16) myLevel := ctxt.GetScratch("levelInConference").(uint16)
if !conf.TestPermission("Conference.Change", myLevel) { if !conf.TestPermission("Conference.Change", myLevel) {
@@ -671,7 +666,7 @@ func ConferenceExportForm(ctxt ui.AmContext) (string, any) {
ctxt.VarMap().Set("topics", topics) ctxt.VarMap().Set("topics", topics)
ctxt.VarMap().Set("confName", conf.Name) ctxt.VarMap().Set("confName", conf.Name)
ctxt.VarMap().Set("selfLink", fmt.Sprintf("/comm/%s/conf/%s/export", comm.Alias, ctxt.GetScratch("currentAlias"))) ctxt.VarMap().Set("selfLink", fmt.Sprintf("%s/export", ctxt.GetScratch("ConferenceLink")))
ctxt.SetFrameTitle(fmt.Sprintf("Export Messages: %s", conf.Name)) ctxt.SetFrameTitle(fmt.Sprintf("Export Messages: %s", conf.Name))
return "framed", "conf_export.jet" return "framed", "conf_export.jet"
} }
@@ -684,7 +679,6 @@ func ConferenceExportForm(ctxt ui.AmContext) (string, any) {
* Data as a parameter for the command string. * Data as a parameter for the command string.
*/ */
func ConferenceExport(ctxt ui.AmContext) (string, any) { func ConferenceExport(ctxt ui.AmContext) (string, any) {
comm := ctxt.CurrentCommunity()
conf := ctxt.GetScratch("currentConference").(*database.Conference) conf := ctxt.GetScratch("currentConference").(*database.Conference)
myLevel := ctxt.GetScratch("levelInConference").(uint16) myLevel := ctxt.GetScratch("levelInConference").(uint16)
if !conf.TestPermission("Conference.Change", myLevel) { if !conf.TestPermission("Conference.Change", myLevel) {
@@ -692,7 +686,7 @@ func ConferenceExport(ctxt ui.AmContext) (string, any) {
} }
if ctxt.FormFieldIsSet("cancel") { if ctxt.FormFieldIsSet("cancel") {
return "redirect", fmt.Sprintf("/comm/%s/conf/%s/manage", comm.Alias, ctxt.GetScratch("currentAlias")) return "redirect", fmt.Sprintf("%s/manage", ctxt.GetScratch("ConferenceLink"))
} else if !ctxt.FormFieldIsSet("export") { } else if !ctxt.FormFieldIsSet("export") {
return "error", EBUTTON return "error", EBUTTON
} }
@@ -765,15 +759,16 @@ func ConferenceImport(ctxt ui.AmContext) (string, any) {
return "error", ENOPERM return "error", ENOPERM
} }
ctxt.VarMap().Set("confName", conf.Name)
ctxt.VarMap().Set("selfLink", fmt.Sprintf("%s/import", ctxt.GetScratch("ConferenceLink")))
ctxt.SetFrameTitle("Import Messages: " + conf.Name)
if ctxt.Verb() == "GET" { if ctxt.Verb() == "GET" {
ctxt.VarMap().Set("confName", conf.Name)
ctxt.VarMap().Set("selfLink", fmt.Sprintf("/comm/%s/conf/%s/import", comm.Alias, ctxt.GetScratch("currentAlias")))
ctxt.SetFrameTitle("Import Messages: " + conf.Name)
return "framed", "conf_import.jet" return "framed", "conf_import.jet"
} }
if ctxt.FormFieldIsSet("cancel") { if ctxt.FormFieldIsSet("cancel") {
return "redirect", fmt.Sprintf("/comm/%s/conf/%s/manage", comm.Alias, ctxt.GetScratch("currentAlias")) return "redirect", fmt.Sprintf("%s/manage", ctxt.GetScratch("ConferenceLink"))
} else if !ctxt.FormFieldIsSet("import") { } else if !ctxt.FormFieldIsSet("import") {
return "error", EBUTTON return "error", EBUTTON
} }
@@ -786,27 +781,18 @@ func ConferenceImport(ctxt ui.AmContext) (string, any) {
mode = exports.VCIFTopicMatchNum mode = exports.VCIFTopicMatchNum
default: default:
ctxt.VarMap().Set("errorMessage", "Invalid matching parameter.") ctxt.VarMap().Set("errorMessage", "Invalid matching parameter.")
ctxt.VarMap().Set("confName", conf.Name)
ctxt.VarMap().Set("selfLink", fmt.Sprintf("/comm/%s/conf/%s/import", comm.Alias, ctxt.GetScratch("currentAlias")))
ctxt.SetFrameTitle("Import Messages: " + conf.Name)
return "framed", "conf_import.jet" return "framed", "conf_import.jet"
} }
importData, err := ctxt.FormFile("idata") importData, err := ctxt.FormFile("idata")
if err != nil { if err != nil {
ctxt.VarMap().Set("errorMessage", err.Error()) ctxt.VarMap().Set("errorMessage", err.Error())
ctxt.VarMap().Set("confName", conf.Name)
ctxt.VarMap().Set("selfLink", fmt.Sprintf("/comm/%s/conf/%s/import", comm.Alias, ctxt.GetScratch("currentAlias")))
ctxt.SetFrameTitle("Import Messages: " + conf.Name)
return "framed", "conf_import.jet" return "framed", "conf_import.jet"
} }
start := time.Now() start := time.Now()
f, err := importData.Open() f, err := importData.Open()
if err != nil { if err != nil {
ctxt.VarMap().Set("errorMessage", err.Error()) ctxt.VarMap().Set("errorMessage", err.Error())
ctxt.VarMap().Set("confName", conf.Name)
ctxt.VarMap().Set("selfLink", fmt.Sprintf("/comm/%s/conf/%s/import", comm.Alias, ctxt.GetScratch("currentAlias")))
ctxt.SetFrameTitle("Import Messages: " + conf.Name)
return "framed", "conf_import.jet" return "framed", "conf_import.jet"
} }
topics, posts, scroll, err := exports.VCIFImportMessages(ctxt.Ctx(), f, comm, conf, mode, ctxt.FormFieldIsSet("create"), ctxt.CurrentUser(), ctxt.RemoteIP()) topics, posts, scroll, err := exports.VCIFImportMessages(ctxt.Ctx(), f, comm, conf, mode, ctxt.FormFieldIsSet("create"), ctxt.CurrentUser(), ctxt.RemoteIP())
@@ -814,13 +800,10 @@ func ConferenceImport(ctxt ui.AmContext) (string, any) {
log.Infof("import messages operation completed in %v", time.Since(start)) log.Infof("import messages operation completed in %v", time.Since(start))
if err != nil { if err != nil {
ctxt.VarMap().Set("errorMessage", err.Error()) ctxt.VarMap().Set("errorMessage", err.Error())
ctxt.VarMap().Set("confName", conf.Name)
ctxt.VarMap().Set("selfLink", fmt.Sprintf("/comm/%s/conf/%s/import", comm.Alias, ctxt.GetScratch("currentAlias")))
ctxt.SetFrameTitle("Import Messages: " + conf.Name)
return "framed", "conf_import.jet" return "framed", "conf_import.jet"
} }
ctxt.VarMap().Set("backLink", fmt.Sprintf("/comm/%s/conf/%s/manage", comm.Alias, ctxt.GetScratch("currentAlias"))) ctxt.VarMap().Set("backLink", fmt.Sprintf("%s/manage", ctxt.GetScratch("ConferenceLink")))
ctxt.VarMap().Set("headline", fmt.Sprintf("Processed %d topic(s) and added %d new post(s).", topics, posts)) ctxt.VarMap().Set("headline", fmt.Sprintf("Processed %d topic(s) and added %d new post(s).", topics, posts))
ctxt.VarMap().Set("scroll", scroll) ctxt.VarMap().Set("scroll", scroll)
ctxt.SetFrameTitle("Import Results") ctxt.SetFrameTitle("Import Results")
@@ -858,8 +841,8 @@ func DeleteConference(ctxt ui.AmContext) (string, any) {
// Set up to display the message box. // Set up to display the message box.
mbox.SetMessage(fmt.Sprintf(`You are about to delete the conference <span class="font-bold text-red-600">"%s"</span> mbox.SetMessage(fmt.Sprintf(`You are about to delete the conference <span class="font-bold text-red-600">"%s"</span>
from the <span class="font-bold text-red-600">"%s"</span> community!`, conf.Name, comm.Name)) from the <span class="font-bold text-red-600">"%s"</span> community!`, conf.Name, comm.Name))
mbox.SetLink("no", fmt.Sprintf("/comm/%s/conf/%s/manage", ctxt.CurrentCommunity().Alias, ctxt.GetScratch("currentAlias"))) mbox.SetLink("no", fmt.Sprintf("%s/manage", ctxt.GetScratch("ConferenceLink")))
mbox.SetLink("yes", fmt.Sprintf("/comm/%s/conf/%s/delete", ctxt.CurrentCommunity().Alias, ctxt.GetScratch("currentAlias"))) mbox.SetLink("yes", fmt.Sprintf("%s/delete", ctxt.GetScratch("ConferenceLink")))
return mbox.Render(ctxt) return mbox.Render(ctxt)
} }
@@ -901,9 +884,12 @@ func CreateConference(ctxt ui.AmContext) (string, any) {
if err != nil { if err != nil {
return "error", err return "error", err
} }
var urlbuf strings.Builder
urlbuf.WriteString(ctxt.GetScratch("CommunityLink").(string))
urlbuf.WriteString("/conf")
button := dlg.WhichButton(ctxt) button := dlg.WhichButton(ctxt)
if button == "cancel" { if button == "cancel" {
return "redirect", fmt.Sprintf("/comm/%s/conf", comm.Alias) return "redirect", urlbuf.String()
} else if button != "create" { } else if button != "create" {
dlg.SetCommunity(comm) dlg.SetCommunity(comm)
return dlg.RenderError(ctxt, "invalid button pressed") return dlg.RenderError(ctxt, "invalid button pressed")
@@ -917,7 +903,9 @@ func CreateConference(ctxt ui.AmContext) (string, any) {
return dlg.RenderError(ctxt, err.Error()) return dlg.RenderError(ctxt, err.Error())
} }
log.Infof("Created conference '%s'", conf.Name) log.Infof("Created conference '%s'", conf.Name)
return "redirect", fmt.Sprintf("/comm/%s/conf/%s", comm.Alias, alias) urlbuf.WriteString("/")
urlbuf.WriteString(alias)
return "redirect", urlbuf.String()
} }
/* ManageConferenceList displays the list for managing conferences. /* ManageConferenceList displays the list for managing conferences.
+2 -1
View File
@@ -30,7 +30,7 @@ import (
) )
// AMSTERDAM_VERSION contains the version number of Amsterdam. // AMSTERDAM_VERSION contains the version number of Amsterdam.
const AMSTERDAM_VERSION = "0.1.0" const AMSTERDAM_VERSION = "0.2.0"
// AMSTERDAM_COPYRIGHT contains the copyright dates for Amsterdam. // AMSTERDAM_COPYRIGHT contains the copyright dates for Amsterdam.
const AMSTERDAM_COPYRIGHT = "2025-2026" const AMSTERDAM_COPYRIGHT = "2025-2026"
@@ -92,6 +92,7 @@ type AmConfig struct {
Type string `yaml:"type"` Type string `yaml:"type"`
} `yaml:"siteIcon"` } `yaml:"siteIcon"`
SiteShortcutIcon string `yaml:"siteShortcutIcon"` SiteShortcutIcon string `yaml:"siteShortcutIcon"`
SiteAppleIcon string `yaml:"siteAppleIcon"`
SiteLogo string `yaml:"siteLogo"` SiteLogo string `yaml:"siteLogo"`
TopRefresh int `yaml:"topRefresh"` TopRefresh int `yaml:"topRefresh"`
LoginCookieName string `yaml:"loginCookieName"` LoginCookieName string `yaml:"loginCookieName"`
+1
View File
@@ -17,6 +17,7 @@ site:
path: "/img/builtin/AmsterdamIcon32.png" path: "/img/builtin/AmsterdamIcon32.png"
type: "image/png" type: "image/png"
siteShortcutIcon: "/img/builtin/AmsterdamIcon32.ico" siteShortcutIcon: "/img/builtin/AmsterdamIcon32.ico"
siteAppleIcon: "/img/builtin/AmsterdamAppleIcon.png"
siteLogo: "/img/builtin/powered-by-amsterdam.png" siteLogo: "/img/builtin/powered-by-amsterdam.png"
topRefresh: 300 topRefresh: 300
loginCookieName: AmsterdamAuth loginCookieName: AmsterdamAuth
+1
View File
@@ -93,6 +93,7 @@ const (
AuditAdminChangeUserAccount = 111 AuditAdminChangeUserAccount = 111
AuditAdminSetAccountSecurity = 112 AuditAdminSetAccountSecurity = 112
AuditAdminLockUnlockAccount = 113 AuditAdminLockUnlockAccount = 113
AuditAdminSetUserName = 114
AuditCommunityCreate = 201 AuditCommunityCreate = 201
AuditCommunitySetMembership = 202 AuditCommunitySetMembership = 202
AuditCommunityContactInfo = 203 AuditCommunityContactInfo = 203
+2
View File
@@ -44,6 +44,8 @@ auditReference:
text: "Admin Set Account Security" text: "Admin Set Account Security"
- code: 113 - code: 113
text: "Admin Lock/Unlock Account" text: "Admin Lock/Unlock Account"
- code: 114
text: "Admin Set User Name"
- code: 201 - code: 201
text: "Create New Community" text: "Create New Community"
- code: 202 - code: 202
+187 -5
View File
@@ -13,35 +13,217 @@ package database
import ( import (
"context" "context"
"embed"
"errors"
"fmt" "fmt"
"io/fs"
"regexp"
"slices" "slices"
"strings"
"git.erbosoft.com/amy/amsterdam/config" "git.erbosoft.com/amy/amsterdam/config"
_ "github.com/go-sql-driver/mysql" "github.com/go-sql-driver/mysql"
"github.com/jmoiron/sqlx" "github.com/jmoiron/sqlx"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
) )
// Error classifications
const (
classUnspecified = 0
classNeedInstall = 1
classNeedConvert = 2
)
// MySQL Errors
var errMySQLNoTable = &mysql.MySQLError{Number: 1146}
var errMySQLNoColumn = &mysql.MySQLError{Number: 1054}
//go:embed mysql-install.sql
var installScriptMySQL string
//go:embed mysql-convert.sql
var convertScriptMySQL string
//go:embed mysql-migrate/*
var migrationsMySQL embed.FS
// amdb is the reference to the Amsterdam database. // amdb is the reference to the Amsterdam database.
var amdb *sqlx.DB var amdb *sqlx.DB
// buildMysqlDSN builds the MySQL DSN for the driver. // buildMysqlDSN builds the MySQL DSN for the driver.
func buildMysqlDSN() string { func buildMysqlDSN(multiStatement bool) string {
return fmt.Sprintf("%s:%s@tcp(%s)/%s?parseTime=true&loc=UTC", rc := fmt.Sprintf("%s:%s@tcp(%s)/%s?parseTime=true&loc=UTC",
config.GlobalComputedConfig.DatabaseUser, config.GlobalComputedConfig.DatabaseUser,
config.GlobalComputedConfig.DatabasePassword, config.GlobalComputedConfig.DatabasePassword,
config.GlobalComputedConfig.DatabaseHost, config.GlobalComputedConfig.DatabaseHost,
config.GlobalComputedConfig.DatabaseName) config.GlobalComputedConfig.DatabaseName)
if multiStatement {
rc += "&multiStatements=true"
}
return rc
}
// classifyGetError classifies errors returns from the original get of the version number.
func classifyGetError(err error) int {
if errors.Is(err, errMySQLNoTable) {
return classNeedInstall
}
if errors.Is(err, errMySQLNoColumn) {
return classNeedConvert
}
return classUnspecified
}
// databaseVersionNumber reads the version number from the database.
func databaseVersionNumber(db *sqlx.DB) (string, error) {
ver := ""
err := db.Get(&ver, "SELECT version FROM globals")
return ver, err
}
// setDatabaseVersionNumber resets the version number in the database.
func setDatabaseVersionNumber(db *sqlx.DB, version string) error {
_, err := db.Exec("UPDATE globals SET version = ?", version)
return err
}
// databaseInstallScript returns the install script for the database.
func databaseInstallScript() (string, error) {
switch config.GlobalComputedConfig.DatabaseDriver {
case "mysql":
return installScriptMySQL, nil
default:
return "", fmt.Errorf("No install script for database driver: %s", config.GlobalComputedConfig.DatabaseDriver)
}
}
// databaseConvertScript returns the script to convert a Venice database to Amsterdam.
func databaseConvertScript() (string, error) {
switch config.GlobalComputedConfig.DatabaseDriver {
case "mysql":
return convertScriptMySQL, nil
default: // N.B.: Not to be implemented for any database type besides MySQL!
return "", fmt.Errorf("No conversion script for database driver: %s", config.GlobalComputedConfig.DatabaseDriver)
}
}
// databaseMigrationScripts returns the migration scripts to apply to the database.
func databaseMigrationScripts(version string) (fs.FS, string, []string, error) {
var myfs fs.FS
var dirname string = ""
var err error = nil
switch config.GlobalComputedConfig.DatabaseDriver {
case "mysql":
myfs = migrationsMySQL
dirname = "mysql-migrate"
default:
err = fmt.Errorf("No migration scripts for database driver: %s", config.GlobalComputedConfig.DatabaseDriver)
}
if err != nil {
return nil, "", make([]string, 0), err
}
rdfs := myfs.(fs.ReadDirFS)
dents, err := rdfs.ReadDir(dirname)
if err != nil {
return nil, "", make([]string, 0), err
}
rc := make([]string, 0, len(dents))
for _, d := range dents {
s := strings.TrimSuffix(d.Name(), ".sql")
m, err := regexp.Match(`\d{10}`, []byte(s))
if err != nil {
return nil, "", make([]string, 0), err
}
if m && s > version {
rc = append(rc, d.Name())
}
}
if len(rc) > 1 {
slices.Sort(rc)
}
return myfs, dirname, rc, nil
}
// prepareDB prepares the database if it's not yet been loaded.
func prepareDB() (string, error) {
dsn := buildMysqlDSN(true)
log.Debugf("dsn=%s", dsn)
db, err := sqlx.Connect(config.GlobalComputedConfig.DatabaseDriver, dsn)
if err != nil {
return "", err
}
defer db.Close()
version, err := databaseVersionNumber(db)
if err != nil {
switch classifyGetError(err) {
case classUnspecified:
log.Errorf("*** cannot get version number: %v (%T)", err, err)
return version, err
case classNeedInstall:
installScript, err := databaseInstallScript()
if err != nil {
return "", err
}
_, err = db.Exec(installScript)
if err != nil {
return "", fmt.Errorf("Failure of install script: %w", err)
}
case classNeedConvert:
convertScript, err := databaseConvertScript()
if err != nil {
return "", err
}
_, err = db.Exec(convertScript)
if err != nil {
return "", fmt.Errorf("Failure of conversion script: %w", err)
}
}
version, err = databaseVersionNumber(db)
if err != nil {
return "", err
}
}
scriptfs, dirname, scripts, err := databaseMigrationScripts(version)
if err == nil {
log.Infof("%d migration script(s) to be applied", len(scripts))
rffs := scriptfs.(fs.ReadFileFS)
for _, script := range scripts {
log.Infof("applying migration script: %s", script)
var data []byte
data, err = rffs.ReadFile(fmt.Sprintf("%s/%s", dirname, script))
if err != nil {
return version, fmt.Errorf("Unable to read migration script %s: %w", script, err)
}
_, err = db.Exec(string(data))
if err != nil {
return version, fmt.Errorf("Unable to apply migration script %s: %w", script, err)
}
nv := strings.TrimSuffix(script, ".sql")
err = setDatabaseVersionNumber(db, nv)
if err != nil {
break
}
version = nv
}
}
return version, err
} }
// SetupDb sets up the database and associated items. // SetupDb sets up the database and associated items.
func SetupDb() (func(), error) { func SetupDb() (func(), error) {
exitfns := make([]func(), 0, 2) exitfns := make([]func(), 0, 2)
db, err := sqlx.Connect(config.GlobalComputedConfig.DatabaseDriver, buildMysqlDSN()) version, err := prepareDB()
if err != nil {
return nil, err
}
db, err := sqlx.Connect(config.GlobalComputedConfig.DatabaseDriver, buildMysqlDSN(false))
if err == nil { if err == nil {
amdb = db amdb = db
g, err := AmGlobals(context.Background()) g, err := AmGlobals(context.Background())
if err == nil { if err == nil {
if g.Version != version {
log.Warnf("!! database version %s does not match prepared version %s", g.Version, version)
}
setupAdCache() setupAdCache()
setupUserCache() setupUserCache()
setupContactsCache() setupContactsCache()
@@ -53,8 +235,8 @@ func SetupDb() (func(), error) {
log.Infof("SetupDb(): database version %s", g.Version) log.Infof("SetupDb(): database version %s", g.Version)
} }
} }
slices.Reverse(exitfns)
return func() { return func() {
slices.Reverse(exitfns)
for _, f := range exitfns { for _, f := range exitfns {
f() f()
} }
+54 -59
View File
@@ -17,6 +17,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"slices" "slices"
"strconv"
"strings" "strings"
"sync" "sync"
"time" "time"
@@ -152,8 +153,8 @@ func (cs *ConferenceSettings) Save(ctx context.Context) error {
} }
// Aliases returns the list of aliases for this conference. // Aliases returns the list of aliases for this conference.
func (c *Conference) Aliases(ctx context.Context) ([]string, error) { func (c *Conference) Aliases(ctx context.Context, commid int32) ([]string, error) {
rs, err := amdb.QueryContext(ctx, "SELECT alias FROM confalias WHERE confid = ? ORDER BY alias", c.ConfId) rs, err := amdb.QueryContext(ctx, "SELECT alias FROM confalias WHERE commid = ? AND confid = ? ORDER BY alias", commid, c.ConfId)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -169,25 +170,22 @@ func (c *Conference) Aliases(ctx context.Context) ([]string, error) {
return rc, nil return rc, nil
} }
// AliasesQ returns the list of aliases for this conference, quietly.
func (c *Conference) AliasesQ(ctx context.Context) []string {
rc, _ := c.Aliases(ctx)
return rc
}
// AddAlias adds an alias to the conference. // AddAlias adds an alias to the conference.
func (c *Conference) AddAlias(ctx context.Context, alias string, u *User, comm *Community, ipaddr string) error { func (c *Conference) AddAlias(ctx context.Context, alias string, u *User, comm *Community, ipaddr string) error {
tmp := "" tmp := ""
if err := amdb.GetContext(ctx, &tmp, "SELECT alias FROM confalias WHERE confid = ? AND alias = ?", c.ConfId, alias); err != sql.ErrNoRows { if err := amdb.GetContext(ctx, &tmp, "SELECT alias FROM confalias WHERE commid = ? AND confid = ? AND alias = ?",
comm.Id, c.ConfId, alias); err != sql.ErrNoRows {
if err == nil { if err == nil {
return fmt.Errorf("the alias '%s' is already in use by another conference", alias) return fmt.Errorf("the alias '%s' is already in use by another conference", alias)
} }
return err return err
} }
if _, err := amdb.ExecContext(ctx, "INSERT INTO confalias (confid, alias) VALUES (?, ?)", c.ConfId, alias); err != nil { if _, err := amdb.ExecContext(ctx, "INSERT INTO confalias (commid, confid, alias) VALUES (?, ?, ?)",
comm.Id, c.ConfId, alias); err != nil {
return err return err
} }
conferenceAliasMap.Store(confAliasMapKey(comm.Id, alias), c.ConfId)
AmStoreAudit(AmNewCommAudit(AuditConferenceAlias, u.Uid, comm.Id, ipaddr, fmt.Sprintf("conf=%d", c.ConfId), fmt.Sprintf("add=%s", alias))) AmStoreAudit(AmNewCommAudit(AuditConferenceAlias, u.Uid, comm.Id, ipaddr, fmt.Sprintf("conf=%d", c.ConfId), fmt.Sprintf("add=%s", alias)))
return nil return nil
} }
@@ -195,21 +193,23 @@ func (c *Conference) AddAlias(ctx context.Context, alias string, u *User, comm *
// RemoveAlias removes an alias from the conference. // RemoveAlias removes an alias from the conference.
func (c *Conference) RemoveAlias(ctx context.Context, alias string, u *User, comm *Community, ipaddr string) error { func (c *Conference) RemoveAlias(ctx context.Context, alias string, u *User, comm *Community, ipaddr string) error {
aliasCount := 0 aliasCount := 0
if err := amdb.GetContext(ctx, &aliasCount, "SELECT COUNT(*) FROM confalias WHERE confid = ?", c.ConfId); err != nil { if err := amdb.GetContext(ctx, &aliasCount, "SELECT COUNT(*) FROM confalias WHERE commid = ? AND confid = ?",
comm.Id, c.ConfId); err != nil {
return err return err
} }
if aliasCount == 1 { if aliasCount == 1 {
tmp := "" tmp := ""
err := amdb.GetContext(ctx, &tmp, "SELECT alias FROM confalias WHERE confid = ? AND alias = ?", c.ConfId, alias) err := amdb.GetContext(ctx, &tmp, "SELECT alias FROM confalias WHERE commid = ? AND confid = ? AND alias = ?",
comm.Id, c.ConfId, alias)
if err == nil { if err == nil {
return errors.New("the conference must have at least one alias") return errors.New("the conference must have at least one alias")
} else if err != sql.ErrNoRows { } else if !errors.Is(err, sql.ErrNoRows) {
return err return err
} }
} }
rs, err := amdb.ExecContext(ctx, "DELETE FROM confalias WHERE confid = ? AND alias = ?", c.ConfId, alias) rs, err := amdb.ExecContext(ctx, "DELETE FROM confalias WHERE commid = ? AND confid = ? AND alias = ?", comm.Id, c.ConfId, alias)
if err != nil { if err != nil {
return err return err
} }
@@ -221,6 +221,7 @@ func (c *Conference) RemoveAlias(ctx context.Context, alias string, u *User, com
return errors.New("alias not found") return errors.New("alias not found")
} }
conferenceAliasMap.Delete(confAliasMapKey(comm.Id, alias))
AmStoreAudit(AmNewCommAudit(AuditConferenceAlias, u.Uid, comm.Id, ipaddr, fmt.Sprintf("conf=%d", c.ConfId), fmt.Sprintf("remove=%s", alias))) AmStoreAudit(AmNewCommAudit(AuditConferenceAlias, u.Uid, comm.Id, ipaddr, fmt.Sprintf("conf=%d", c.ConfId), fmt.Sprintf("remove=%s", alias)))
return nil return nil
} }
@@ -431,8 +432,8 @@ func (c *Conference) Settings(ctx context.Context, u *User) (*ConferenceSettings
} }
// Link returns a link string to this conference. // Link returns a link string to this conference.
func (c *Conference) Link(ctx context.Context, scope string) (string, error) { func (c *Conference) Link(ctx context.Context, commid int32, scope string) (string, error) {
aliases, err := c.Aliases(ctx) aliases, err := c.Aliases(ctx, commid)
if err != nil { if err != nil {
return "", err return "", err
} }
@@ -440,9 +441,9 @@ func (c *Conference) Link(ctx context.Context, scope string) (string, error) {
return fmt.Sprintf("%s.", aliases[0]), nil return fmt.Sprintf("%s.", aliases[0]), nil
} }
if scope == "global" { if scope == "global" {
comms, err := c.ContainedBy(ctx) comm, err := AmGetCommunity(ctx, commid)
if err == nil { if err == nil {
return fmt.Sprintf("%s!%s", comms[0].Alias, aliases[0]), nil return fmt.Sprintf("%s!%s", comm.Alias, aliases[0]), nil
} }
return "", err return "", err
} }
@@ -872,22 +873,22 @@ func (c *Conference) Delete(ctx context.Context, comm *Community, u *User, ipadd
// any references to conference other than this community? // any references to conference other than this community?
refCount := 0 refCount := 0
if err := tx.GetContext(ctx, &refCount, "SELECT COUNT(*) FROM commtoconf WHERE confid = ? AND commid <> ?", c.ConfId, comm.Id); err != nil { err := tx.GetContext(ctx, &refCount, "SELECT COUNT(*) FROM commtoconf WHERE confid = ? AND commid <> ?", c.ConfId, comm.Id)
if err != nil {
return err return err
} }
// break the link with the community // break the link with the community
if _, err := tx.ExecContext(ctx, "DELETE FROM commtoconf WHERE commid = ? AND confid = ?", comm.Id, c.ConfId); err != nil { if _, err = tx.ExecContext(ctx, "DELETE FROM commtoconf WHERE commid = ? AND confid = ?", comm.Id, c.ConfId); err == nil {
_, err = tx.ExecContext(ctx, "DELETE FROM confalias WHERE commid = ? AND confid = ?", comm.Id, c.ConfId)
}
if err != nil {
return err return err
} }
var err error
if refCount == 0 { if refCount == 0 {
// We have to delete all the conference core data now. // We have to delete all the conference core data now.
_, err = tx.ExecContext(ctx, "DELETE FROM confs WHERE confid = ?", c.ConfId) _, err = tx.ExecContext(ctx, "DELETE FROM confs WHERE confid = ?", c.ConfId)
if err == nil {
_, err = tx.ExecContext(ctx, "DELETE FROM confalias WHERE confid = ?", c.ConfId)
}
} }
if err != nil { if err != nil {
return err return err
@@ -901,6 +902,9 @@ func (c *Conference) Delete(ctx context.Context, comm *Community, u *User, ipadd
// kick the conference out of the cache // kick the conference out of the cache
conferenceCache.Remove(c.ConfId) conferenceCache.Remove(c.ConfId)
// simpler to just nuke the entire alias map
conferenceAliasMap.Clear()
// add an audit record // add an audit record
AmStoreAudit(AmNewCommAudit(AuditConferenceDelete, u.Uid, comm.Id, ipaddr, fmt.Sprintf("confid=%d", c.ConfId))) AmStoreAudit(AmNewCommAudit(AuditConferenceDelete, u.Uid, comm.Id, ipaddr, fmt.Sprintf("confid=%d", c.ConfId)))
@@ -955,16 +959,15 @@ func (*conferenceServiceVTable) OnDeleteCommunity(ctx context.Context, tx *sqlx.
if _, err = tx.ExecContext(ctx, "DELETE FROM commtoconf WHERE commid = ? AND confid = ?", commid, confid); err != nil { if _, err = tx.ExecContext(ctx, "DELETE FROM commtoconf WHERE commid = ? AND confid = ?", commid, confid); err != nil {
return err return err
} }
if _, err = tx.ExecContext(ctx, "DELETE FROM confalias WHERE commid = ? AND confid = ?", commid, confid); err != nil {
return err
}
if refCount > 0 { if refCount > 0 {
confids[i] = -1 confids[i] = -1
continue // done with this conference continue // done with this conference
} }
// We have to delete all the conference core data now. // We have to delete all the conference core data now.
_, err = tx.ExecContext(ctx, "DELETE FROM confs WHERE confid = ?", confid) if _, err = tx.ExecContext(ctx, "DELETE FROM confs WHERE confid = ?", confid); err != nil {
if err == nil {
_, err = tx.ExecContext(ctx, "DELETE FROM confalias WHERE confid = ?", confid)
}
if err != nil {
return err return err
} }
// kick the conference out of the cache // kick the conference out of the cache
@@ -973,7 +976,8 @@ func (*conferenceServiceVTable) OnDeleteCommunity(ctx context.Context, tx *sqlx.
getConferenceMutex.Unlock() getConferenceMutex.Unlock()
} }
// Just dump the whole conference property cache. // Just dump the whole conference alias map and property cache.
conferenceAliasMap.Clear()
getConferencePropMutex.Lock() getConferencePropMutex.Lock()
conferencePropCache.Purge() conferencePropCache.Purge()
getConferencePropMutex.Unlock() getConferencePropMutex.Unlock()
@@ -1030,27 +1034,38 @@ func AmGetConference(ctx context.Context, id int32) (*Conference, error) {
return nil, err return nil, err
} }
// confAliasMapKey creates the key for the conference alias map.
func confAliasMapKey(commid int32, alias string) string {
var b strings.Builder
b.WriteString(strconv.FormatInt(int64(commid), 10))
b.WriteByte(byte(':'))
b.WriteString(alias)
return b.String()
}
/* AmGetConferenceByAlias returns a conference given its alias. /* AmGetConferenceByAlias returns a conference given its alias.
* Parameters: * Parameters:
* ctx - Standard Go context value. * ctx - Standard Go context value.
*. commid - Community ID to look under.
* alias - The alias to look up. * alias - The alias to look up.
* Returns: * Returns:
* Pointer to the conference, or nil. * Pointer to the conference, or nil.
* Standard Go error status. * Standard Go error status.
*/ */
func AmGetConferenceByAlias(ctx context.Context, alias string) (*Conference, error) { func AmGetConferenceByAlias(ctx context.Context, commid int32, alias string) (*Conference, error) {
var confid int32 var confid int32
xconf, ok := conferenceAliasMap.Load(alias) key := confAliasMapKey(commid, alias)
xconf, ok := conferenceAliasMap.Load(key)
if ok { if ok {
confid = xconf.(int32) confid = xconf.(int32)
} else { } else {
err := amdb.GetContext(ctx, &confid, "SELECT confid FROM confalias WHERE alias = ?", alias) err := amdb.GetContext(ctx, &confid, "SELECT confid FROM confalias WHERE commid = ? AND alias = ?", commid, alias)
if err == sql.ErrNoRows { if err == sql.ErrNoRows {
return nil, fmt.Errorf("alias not found: %s", alias) return nil, fmt.Errorf("alias not found: %s", alias)
} else if err != nil { } else if err != nil {
return nil, err return nil, err
} }
conferenceAliasMap.Store(alias, confid) conferenceAliasMap.Store(key, confid)
} }
return AmGetConference(ctx, confid) return AmGetConference(ctx, confid)
} }
@@ -1074,28 +1089,6 @@ func AmGetConferenceContainingPost(ctx context.Context, postId int64) (*Conferen
return AmGetConference(ctx, confId) return AmGetConference(ctx, confId)
} }
/* AmGetConferenceByAliasInCommunity returns a conference in a community given its alias.
* Parameters:
* ctx - Standard Go context value.
* cid - The community to look inside.
* alias - The alias to look up.
* Returns:
* Pointer to the conference, or nil.
* Standard Go error status.
*/
func AmGetConferenceByAliasInCommunity(ctx context.Context, cid int32, alias string) (*Conference, error) {
var confid int32
err := amdb.GetContext(ctx, &confid, `SELECT c.confid FROM commtoconf c, confalias a WHERE c.confid = a.confid
AND c.commid = ? AND a.alias = ?`, cid, alias)
switch err {
case nil:
return AmGetConference(ctx, confid)
case sql.ErrNoRows:
return nil, errors.New("conference not found")
}
return nil, err
}
/* AmListConferences returns all conferences for a given community. /* AmListConferences returns all conferences for a given community.
* Parameters: * Parameters:
* ctx - Standard Go context value. * ctx - Standard Go context value.
@@ -1125,7 +1118,7 @@ func AmListConferences(ctx context.Context, cid int32, showHidden bool) ([]*Conf
} }
} }
for i := range rc { for i := range rc {
err := amdb.GetContext(ctx, &(rc[i].Alias), "SELECT alias FROM confalias WHERE confid = ?", rc[i].ConfId) err := amdb.GetContext(ctx, &(rc[i].Alias), "SELECT alias FROM confalias WHERE commid = ? AND confid = ?", cid, rc[i].ConfId)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -1271,7 +1264,7 @@ func AmCreateConference(ctx context.Context, comm *Community, name, alias, descr
// Ensure the alias is not in use. // Ensure the alias is not in use.
var tmp int32 var tmp int32
err := tx.GetContext(ctx, &tmp, "SELECT confid FROM confalias WHERE alias = ?", alias) err := tx.GetContext(ctx, &tmp, "SELECT confid FROM confalias WHERE commid = ? AND alias = ?", comm.Id, alias)
if err == nil { if err == nil {
return nil, fmt.Errorf("the alias '%s' is already in use by a different conference", alias) return nil, fmt.Errorf("the alias '%s' is already in use by a different conference", alias)
} else if err != sql.ErrNoRows { } else if err != sql.ErrNoRows {
@@ -1295,7 +1288,8 @@ func AmCreateConference(ctx context.Context, comm *Community, name, alias, descr
} }
// Attach the alias to the conference. // Attach the alias to the conference.
if _, err = tx.ExecContext(ctx, "INSERT INTO confalias (confid, alias) VALUES (?, ?)", rc.ConfId, alias); err != nil { if _, err = tx.ExecContext(ctx, "INSERT INTO confalias (commid, confid, alias) VALUES (?, ?, ?)",
comm.Id, rc.ConfId, alias); err != nil {
return nil, err return nil, err
} }
@@ -1329,6 +1323,7 @@ func AmCreateConference(ctx context.Context, comm *Community, name, alias, descr
// Add the new conference to the cache. // Add the new conference to the cache.
conferenceCache.Add(rc.ConfId, &rc) conferenceCache.Add(rc.ConfId, &rc)
conferenceAliasMap.Store(confAliasMapKey(comm.Id, alias), rc.ConfId)
// Set the "pictures in posts" flag for the conference from the community default. // Set the "pictures in posts" flag for the conference from the community default.
fcomm, err := comm.Flags(ctx) fcomm, err := comm.Flags(ctx)
@@ -1,6 +1,3 @@
# MySQL script for migrating a Venice database to the Amsterdam schema.
# Written by Amy Bowersox <amy@erbosoft.com>
#---------------------------------------------------------------------------
# Amsterdam Web Communities System # Amsterdam Web Communities System
# Copyright (c) 2025-2026 Erbosoft Metaverse Design Solutions, All Rights Reserved # Copyright (c) 2025-2026 Erbosoft Metaverse Design Solutions, All Rights Reserved
# #
@@ -10,7 +7,6 @@
# #
# SPDX-License-Identifier: MPL-2.0 # SPDX-License-Identifier: MPL-2.0
# #
ALTER TABLE globals RENAME COLUMN max_sig_mbr_page TO max_comm_mbr_page; ALTER TABLE globals RENAME COLUMN max_sig_mbr_page TO max_comm_mbr_page;
ALTER TABLE globals RENAME COLUMN sig_create_lvl TO comm_create_lvl; ALTER TABLE globals RENAME COLUMN sig_create_lvl TO comm_create_lvl;
@@ -1,8 +1,5 @@
# MySQL script for initializing the Amsterdam database (contents only).
# Written by Amy Bowersox <amy@erbosoft.com>
#---------------------------------------------------------------------------
# Amsterdam Web Communities System # 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 # 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 # License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -10,14 +7,6 @@
# #
# SPDX-License-Identifier: MPL-2.0 # SPDX-License-Identifier: MPL-2.0
# #
##############################################################################
# Table Creation
##############################################################################
# The global parameters table. This is used for stuff that a Venice admin would be
# likely to edit "on the fly." Stuff that can only be updated with a shutdown should go
# in the XML config file. This table has ONLY ONE ROW!
CREATE TABLE globals ( CREATE TABLE globals (
version CHAR(10) NOT NULL, version CHAR(10) NOT NULL,
posts_per_page INT NOT NULL, posts_per_page INT NOT NULL,
@@ -30,14 +19,11 @@ CREATE TABLE globals (
comm_create_lvl INT NOT NULL comm_create_lvl INT NOT NULL
) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; ) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
# The global properties table. The "ndx" parameter is used to indicate what
# element is being loaded, and then the "data" element is parsed.
CREATE TABLE propglobal ( CREATE TABLE propglobal (
ndx INT NOT NULL PRIMARY KEY, ndx INT NOT NULL PRIMARY KEY,
data VARCHAR(255) data VARCHAR(255)
) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; ) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
# The audit records table. Most "major" events add a record to this table.
CREATE TABLE audit ( CREATE TABLE audit (
record BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT, record BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT,
on_date DATETIME NOT NULL, on_date DATETIME NOT NULL,
@@ -53,7 +39,6 @@ CREATE TABLE audit (
INDEX comm_view (commid, on_date) INDEX comm_view (commid, on_date)
) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; ) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
# The user information table.
CREATE TABLE users ( CREATE TABLE users (
uid INT NOT NULL PRIMARY KEY AUTO_INCREMENT, uid INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(64) NOT NULL, username VARCHAR(64) NOT NULL,
@@ -74,15 +59,12 @@ CREATE TABLE users (
UNIQUE INDEX username_x (username) UNIQUE INDEX username_x (username)
) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; ) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
# User preferences table.
CREATE TABLE userprefs ( CREATE TABLE userprefs (
uid INT NOT NULL PRIMARY KEY, uid INT NOT NULL PRIMARY KEY,
tzid VARCHAR(64) DEFAULT 'UTC', tzid VARCHAR(64) DEFAULT 'UTC',
localeid VARCHAR(64) DEFAULT 'en_US' localeid VARCHAR(64) DEFAULT 'en_US'
) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; ) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
# The per-user properties table. The "ndx" parameter is used to indicate what
# element is being loaded, and then the "data" element is parsed.
CREATE TABLE propuser ( CREATE TABLE propuser (
uid INT NOT NULL, uid INT NOT NULL,
ndx INT NOT NULL, ndx INT NOT NULL,
@@ -90,7 +72,6 @@ CREATE TABLE propuser (
PRIMARY KEY (uid, ndx) PRIMARY KEY (uid, ndx)
) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; ) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
# Indicates what the top-level "sidebox" configuration is for any given user.
CREATE TABLE sideboxes ( CREATE TABLE sideboxes (
uid INT NOT NULL, uid INT NOT NULL,
boxid INT NOT NULL, boxid INT NOT NULL,
@@ -100,7 +81,6 @@ CREATE TABLE sideboxes (
INDEX inorder (uid, sequence) INDEX inorder (uid, sequence)
) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; ) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
# The contact information table. This is used for both users and communities.
CREATE TABLE contacts ( CREATE TABLE contacts (
contactid INT NOT NULL PRIMARY KEY AUTO_INCREMENT, contactid INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
given_name VARCHAR(64), given_name VARCHAR(64),
@@ -130,14 +110,12 @@ CREATE TABLE contacts (
lastupdate DATETIME lastupdate DATETIME
) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; ) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
# A table listing email addresses which are barred from registering.
CREATE TABLE emailban ( CREATE TABLE emailban (
address VARCHAR(255) NOT NULL PRIMARY KEY, address VARCHAR(255) NOT NULL PRIMARY KEY,
by_uid INT, by_uid INT,
on_date DATETIME on_date DATETIME
) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; ) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
# The community table.
CREATE TABLE communities ( CREATE TABLE communities (
commid INT NOT NULL PRIMARY KEY AUTO_INCREMENT, commid INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
createdate DATETIME NOT NULL, createdate DATETIME NOT NULL,
@@ -168,7 +146,6 @@ CREATE TABLE communities (
INDEX list_alpha (catid, commname) INDEX list_alpha (catid, commname)
) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; ) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
# The table mapping category IDs to category names.
CREATE TABLE refcategory ( CREATE TABLE refcategory (
catid INT NOT NULL PRIMARY KEY, catid INT NOT NULL PRIMARY KEY,
parent INT NOT NULL, parent INT NOT NULL,
@@ -179,14 +156,12 @@ CREATE TABLE refcategory (
UNIQUE INDEX display (parent, name) UNIQUE INDEX display (parent, name)
) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; ) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
# The table mapping communities and their associated features.
CREATE TABLE commftrs ( CREATE TABLE commftrs (
commid INT NOT NULL, commid INT NOT NULL,
ftr_code SMALLINT NOT NULL, ftr_code SMALLINT NOT NULL,
PRIMARY KEY (commid, ftr_code) PRIMARY KEY (commid, ftr_code)
) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; ) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
# The table mapping members of a community and their access levels.
CREATE TABLE commmember ( CREATE TABLE commmember (
commid INT NOT NULL, commid INT NOT NULL,
uid INT NOT NULL, uid INT NOT NULL,
@@ -196,7 +171,6 @@ CREATE TABLE commmember (
PRIMARY KEY (commid, uid) PRIMARY KEY (commid, uid)
) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; ) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
# A table listing users which have been banned from joining a community.
CREATE TABLE commban ( CREATE TABLE commban (
commid INT NOT NULL, commid INT NOT NULL,
uid INT NOT NULL, uid INT NOT NULL,
@@ -205,8 +179,6 @@ CREATE TABLE commban (
PRIMARY KEY (commid, uid) PRIMARY KEY (commid, uid)
) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; ) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
# The community properties table. The "index" parameter is used to indicate what
# element is being loaded, and then the "data" element is parsed.
CREATE TABLE propcomm ( CREATE TABLE propcomm (
cid INT NOT NULL, cid INT NOT NULL,
ndx INT NOT NULL, ndx INT NOT NULL,
@@ -214,8 +186,6 @@ CREATE TABLE propcomm (
PRIMARY KEY (cid, ndx) PRIMARY KEY (cid, ndx)
) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; ) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
# The table describing conferences. Like original CW, confs may be linked to more
# than one community.
CREATE TABLE confs ( CREATE TABLE confs (
confid INT NOT NULL PRIMARY KEY AUTO_INCREMENT, confid INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
createdate DATETIME NOT NULL, createdate DATETIME NOT NULL,
@@ -235,7 +205,6 @@ CREATE TABLE confs (
INDEX name_x (name) INDEX name_x (name)
) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; ) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
# The table that links communities to conferences.
CREATE TABLE commtoconf ( CREATE TABLE commtoconf (
commid INT NOT NULL, commid INT NOT NULL,
confid INT NOT NULL, confid INT NOT NULL,
@@ -246,15 +215,12 @@ CREATE TABLE commtoconf (
INDEX display_ord (commid, sequence) INDEX display_ord (commid, sequence)
) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; ) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
# The table listing "aliases" to a conference for post-linking purposes.
CREATE TABLE confalias ( CREATE TABLE confalias (
confid INT NOT NULL, confid INT NOT NULL,
alias VARCHAR(64) NOT NULL PRIMARY KEY, alias VARCHAR(64) NOT NULL PRIMARY KEY,
INDEX confid_x (confid) INDEX confid_x (confid)
) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; ) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
# A "membership" table for conferences used to control access to private conferences
# and grant conference hosting powers.
CREATE TABLE confmember ( CREATE TABLE confmember (
confid INT NOT NULL, confid INT NOT NULL,
uid INT NOT NULL, uid INT NOT NULL,
@@ -262,7 +228,6 @@ CREATE TABLE confmember (
PRIMARY KEY (confid, uid) PRIMARY KEY (confid, uid)
) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; ) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
# Holds "saved settings" for a user with respect to a conference.
CREATE TABLE confsettings ( CREATE TABLE confsettings (
confid INT NOT NULL, confid INT NOT NULL,
uid INT NOT NULL, uid INT NOT NULL,
@@ -272,7 +237,6 @@ CREATE TABLE confsettings (
PRIMARY KEY (confid, uid) PRIMARY KEY (confid, uid)
) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; ) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
# The "hot list" of conferences for a given user.
CREATE TABLE confhotlist ( CREATE TABLE confhotlist (
uid INT NOT NULL, uid INT NOT NULL,
sequence SMALLINT NOT NULL, sequence SMALLINT NOT NULL,
@@ -282,8 +246,6 @@ CREATE TABLE confhotlist (
INDEX inorder (uid, sequence) INDEX inorder (uid, sequence)
) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; ) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
# The conference properties table. The "index" parameter is used to indicate what
# element is being loaded, and then the "data" element is parsed.
CREATE TABLE propconf ( CREATE TABLE propconf (
confid INT NOT NULL, confid INT NOT NULL,
ndx INT NOT NULL, ndx INT NOT NULL,
@@ -291,15 +253,12 @@ CREATE TABLE propconf (
PRIMARY KEY (confid, ndx) PRIMARY KEY (confid, ndx)
) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; ) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
# The conference custom HTML block table. There are two custom blocks, one at the
# top of the page, one at the bottom, each a maximum of 64K in length.
CREATE TABLE confcustom ( CREATE TABLE confcustom (
confid INT NOT NULL PRIMARY KEY, confid INT NOT NULL PRIMARY KEY,
htmltop TEXT, htmltop TEXT,
htmlbottom TEXT htmlbottom TEXT
) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; ) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
# The table describing topics within a conference.
CREATE TABLE topics ( CREATE TABLE topics (
topicid INT NOT NULL PRIMARY KEY AUTO_INCREMENT, topicid INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
confid INT NOT NULL, confid INT NOT NULL,
@@ -317,7 +276,6 @@ CREATE TABLE topics (
INDEX by_date (confid, lastupdate) INDEX by_date (confid, lastupdate)
) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; ) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
# Holds "saved settings" for a user with respect to a topic.
CREATE TABLE topicsettings ( CREATE TABLE topicsettings (
topicid INT NOT NULL, topicid INT NOT NULL,
uid INT NOT NULL, uid INT NOT NULL,
@@ -329,8 +287,6 @@ CREATE TABLE topicsettings (
PRIMARY KEY (topicid, uid) PRIMARY KEY (topicid, uid)
) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; ) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
# The "bozo filter" list for a topic, for use by users in filtering out
# the rantings of other users who are bozos.
CREATE TABLE topicbozo ( CREATE TABLE topicbozo (
topicid INT NOT NULL, topicid INT NOT NULL,
uid INT NOT NULL, uid INT NOT NULL,
@@ -338,7 +294,6 @@ CREATE TABLE topicbozo (
PRIMARY KEY (topicid, uid, bozo_uid) PRIMARY KEY (topicid, uid, bozo_uid)
) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; ) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
# The "header" for a posted message.
CREATE TABLE posts ( CREATE TABLE posts (
postid BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT, postid BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT,
parent BIGINT NOT NULL DEFAULT 0, parent BIGINT NOT NULL DEFAULT 0,
@@ -356,14 +311,12 @@ CREATE TABLE posts (
INDEX child_order (parent, num) INDEX child_order (parent, num)
) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; ) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
# The actual message text.
CREATE TABLE postdata ( CREATE TABLE postdata (
postid BIGINT NOT NULL PRIMARY KEY, postid BIGINT NOT NULL PRIMARY KEY,
data MEDIUMTEXT, data MEDIUMTEXT,
FULLTEXT INDEX searchpost (data) FULLTEXT INDEX searchpost (data)
) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; ) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
# Message attachment.
CREATE TABLE postattach ( CREATE TABLE postattach (
postid BIGINT NOT NULL PRIMARY KEY, postid BIGINT NOT NULL PRIMARY KEY,
datalen INT, datalen INT,
@@ -376,14 +329,12 @@ CREATE TABLE postattach (
data MEDIUMBLOB data MEDIUMBLOB
) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; ) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
# "Bookmark" table for posts we like.
CREATE TABLE postdogear ( CREATE TABLE postdogear (
uid INT NOT NULL, uid INT NOT NULL,
postid BIGINT NOT NULL, postid BIGINT NOT NULL,
PRIMARY KEY (uid, postid) PRIMARY KEY (uid, postid)
) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; ) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
# "Front page" publishing table.
CREATE TABLE postpublish ( CREATE TABLE postpublish (
commid INT NOT NULL, commid INT NOT NULL,
postid BIGINT NOT NULL PRIMARY KEY, postid BIGINT NOT NULL PRIMARY KEY,
@@ -392,7 +343,6 @@ CREATE TABLE postpublish (
INDEX display_order (on_date, postid) INDEX display_order (on_date, postid)
) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; ) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
# Advertisement (actually quote, for now) banners
CREATE TABLE adverts ( CREATE TABLE adverts (
adid INT NOT NULL PRIMARY KEY AUTO_INCREMENT, adid INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
imagepath VARCHAR(255) NOT NULL, imagepath VARCHAR(255) NOT NULL,
@@ -401,7 +351,6 @@ CREATE TABLE adverts (
linkurl VARCHAR(255) linkurl VARCHAR(255)
) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; ) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
# Storage space for uploaded images.
CREATE TABLE imagestore ( CREATE TABLE imagestore (
imgid INT NOT NULL PRIMARY KEY AUTO_INCREMENT, imgid INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
typecode SMALLINT DEFAULT 0, typecode SMALLINT DEFAULT 0,
@@ -411,7 +360,6 @@ CREATE TABLE imagestore (
data MEDIUMBLOB data MEDIUMBLOB
) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; ) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
# Table listing IP addresses that are banned from logging in or registering.
CREATE TABLE ipban ( CREATE TABLE ipban (
id INT NOT NULL PRIMARY KEY AUTO_INCREMENT, id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
address_lo BIGINT UNSIGNED NOT NULL, address_lo BIGINT UNSIGNED NOT NULL,
@@ -427,13 +375,7 @@ CREATE TABLE ipban (
INDEX by_date (block_on) INDEX by_date (block_on)
) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; ) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
##############################################################################
# Constant Data Population # Constant Data Population
##############################################################################
# Populate the Category table.
# Source: Mozilla Open Directory Project categorization system <http://dmoz.org>;
# additional categorization from WebbMe categories
INSERT INTO refcategory (catid, parent, symlink, name) VALUES INSERT INTO refcategory (catid, parent, symlink, name) VALUES
(0, -1, -1, 'Unclassified'), (0, -1, -1, 'Unclassified'),
(1, -1, -1, 'Arts'), (1, -1, -1, 'Arts'),
@@ -801,10 +743,8 @@ INSERT INTO refcategory (catid, parent, symlink, name) VALUES
(15, -1, -1, 'System'); (15, -1, -1, 'System');
### -- LAST IS 363 -- ### ### -- LAST IS 363 -- ###
# Make sure the special "System" category is hidden.
UPDATE refcategory SET hide_dir = 1, hide_search = 1 WHERE catid = 15; UPDATE refcategory SET hide_dir = 1, hide_search = 1 WHERE catid = 15;
# Create the initial advertisements (quotes).
INSERT INTO adverts (imagepath) VALUES INSERT INTO adverts (imagepath) VALUES
('images/ads/Brown.gif'), ('images/ads/Brown.gif'),
('images/ads/Caine.gif'), ('images/ads/Caine.gif'),
@@ -831,22 +771,14 @@ INSERT INTO adverts (imagepath) VALUES
('images/ads/wonka.gif'), ('images/ads/wonka.gif'),
('images/ads/worf.gif'); ('images/ads/worf.gif');
##############################################################################
# Database Initialization # Database Initialization
##############################################################################
# Initialize the system globals table.
INSERT INTO globals (version, posts_per_page, old_posts_at_top, max_search_page, max_comm_mbr_page, max_conf_mbr_page, INSERT INTO globals (version, posts_per_page, old_posts_at_top, max_search_page, max_comm_mbr_page, max_conf_mbr_page,
fp_posts, num_audit_page, comm_create_lvl) fp_posts, num_audit_page, comm_create_lvl)
VALUES ('2026030501', 20, 2, 20, 50, 50, 10, 100, 1000); VALUES ('2026030501', 20, 2, 20, 50, 50, 10, 100, 1000);
# Initialize the global properies table.
INSERT INTO propglobal (ndx, data) INSERT INTO propglobal (ndx, data)
VALUES (0, ''); VALUES (0, '');
# Add the 'Anonymous Honyak' user to the users table.
# (Do 'SELECT * FROM users WHERE is_anon = 1' to retrieve the AC user details.)
# (UID = 1, CONTACTID = 1)
INSERT INTO users (uid, username, passhash, contactid, is_anon, verify_email, base_lvl, created) INSERT INTO users (uid, username, passhash, contactid, is_anon, verify_email, base_lvl, created)
VALUES (1, 'Anonymous_Honyak', '', 1, 1, 1, 100, '2000-12-01 00:00:00'); VALUES (1, 'Anonymous_Honyak', '', 1, 1, 1, 100, '2000-12-01 00:00:00');
INSERT INTO userprefs (uid) VALUES (1); INSERT INTO userprefs (uid) VALUES (1);
@@ -854,15 +786,11 @@ INSERT INTO propuser (uid, ndx, data) VALUES (1, 0, '');
INSERT INTO contacts (contactid, given_name, family_name, locality, region, pcode, country, email, owner_uid) INSERT INTO contacts (contactid, given_name, family_name, locality, region, pcode, country, email, owner_uid)
VALUES (1, 'Anonymous', 'User', 'Anywhere', '', '', 'US', 'nobody@example.com', 1); VALUES (1, 'Anonymous', 'User', 'Anywhere', '', '', 'US', 'nobody@example.com', 1);
# Provide the default view for Anonymous Honyak. This view will be copied to all
# new users.
INSERT INTO sideboxes (uid, boxid, sequence, param) INSERT INTO sideboxes (uid, boxid, sequence, param)
VALUES (1, 1, 100, NULL), (1, 2, 200, NULL), (1, 3, 300, NULL); VALUES (1, 1, 100, NULL), (1, 2, 200, NULL), (1, 3, 300, NULL);
INSERT INTO confhotlist (uid, sequence, commid, confid) INSERT INTO confhotlist (uid, sequence, commid, confid)
VALUES (1, 100, 2, 2); VALUES (1, 100, 2, 2);
# Add the 'Administrator' user to the users table.
# (UID = 2, CONTACTID = 2)
INSERT INTO users (uid, username, passhash, contactid, verify_email, base_lvl, created) INSERT INTO users (uid, username, passhash, contactid, verify_email, base_lvl, created)
VALUES (2, 'Administrator', '', 2, 1, 64999, '2000-12-01 00:00:00'); VALUES (2, 'Administrator', '', 2, 1, 64999, '2000-12-01 00:00:00');
INSERT INTO userprefs (uid) VALUES (2); INSERT INTO userprefs (uid) VALUES (2);
@@ -870,35 +798,27 @@ INSERT INTO propuser (uid, ndx, data) VALUES (2, 0, '');
INSERT INTO contacts (contactid, given_name, family_name, locality, region, pcode, country, email, owner_uid) INSERT INTO contacts (contactid, given_name, family_name, locality, region, pcode, country, email, owner_uid)
VALUES (2, 'System', 'Administrator', 'Anywhere', '', '', 'US', 'root@your.box.com', 2); VALUES (2, 'System', 'Administrator', 'Anywhere', '', '', 'US', 'root@your.box.com', 2);
# Create the default view for Administrator.
INSERT INTO sideboxes (uid, boxid, sequence, param) INSERT INTO sideboxes (uid, boxid, sequence, param)
VALUES (2, 1, 100, NULL), (2, 2, 200, NULL), (2, 3, 300, NULL); VALUES (2, 1, 100, NULL), (2, 2, 200, NULL), (2, 3, 300, NULL);
INSERT INTO confhotlist (uid, sequence, commid, confid) INSERT INTO confhotlist (uid, sequence, commid, confid)
VALUES (2, 100, 2, 2); VALUES (2, 100, 2, 2);
# Add the administration community to the communities table.
# (COMMID = 1, CONTACTID = 3)
INSERT INTO communities (commid, createdate, read_lvl, write_lvl, create_lvl, delete_lvl, join_lvl, contactid, INSERT INTO communities (commid, createdate, read_lvl, write_lvl, create_lvl, delete_lvl, join_lvl, contactid,
host_uid, catid, hide_dir, hide_search, membersonly, is_admin, init_ftr, host_uid, catid, hide_dir, hide_search, membersonly, is_admin, init_ftr,
commname, language, synopsis, rules, joinkey, alias) commname, language, synopsis, rules, joinkey, alias)
VALUES (1, '2000-12-01 00:00:00', 63000, 63000, 63000, 65500, 63000, 3, 2, 15, 1, 1, 1, 1, 0, VALUES (1, '2000-12-01 00:00:00', 63000, 63000, 63000, 65500, 63000, 3, 2, 15, 1, 1, 1, 1, 0,
'Administration', 'en-US', 'Administrative Community', 'Administrators only!', '', 'Administration', 'en-US', 'Administrative Community', 'Administrators only!', '',
'Admin'); 'Admin');
INSERT INTO contacts (contactid, locality, country, owner_uid, owner_commid) INSERT INTO contacts (contactid, locality, country, owner_uid, owner_commid)
VALUES (3, 'Anywhere', 'US', 2, 1); VALUES (3, 'Anywhere', 'US', 2, 1);
INSERT INTO propcomm (cid, ndx, data) VALUES (1, 0, ''); INSERT INTO propcomm (cid, ndx, data) VALUES (1, 0, '');
# Insert the desired features for the 'Administration' community.
INSERT INTO commftrs (commid, ftr_code) INSERT INTO commftrs (commid, ftr_code)
VALUES (1, 0), (1, 1), (1, 2), (1, 3), (1, 4); VALUES (1, 0), (1, 1), (1, 2), (1, 3), (1, 4);
# Make the 'Administrator' user the host of the 'Administration' community. Also, the Administrator
# cannot unjoin the community.
INSERT INTO commmember (commid, uid, granted_lvl, locked) INSERT INTO commmember (commid, uid, granted_lvl, locked)
VALUES (1, 2, 58500, 1); VALUES (1, 2, 58500, 1);
# Insert the "Administrative Notes" conference into the Administration community.
# (CONFID = 1)
INSERT INTO confs (confid, createdate, read_lvl, post_lvl, create_lvl, hide_lvl, nuke_lvl, change_lvl, INSERT INTO confs (confid, createdate, read_lvl, post_lvl, create_lvl, hide_lvl, nuke_lvl, change_lvl,
delete_lvl, top_topic, name, descr) delete_lvl, top_topic, name, descr)
VALUES (1, '2000-12-01 00:00:00', 63000, 63000, 63000, 63000, 64999, 64999, 65500, 0, VALUES (1, '2000-12-01 00:00:00', 63000, 63000, 63000, 63000, 64999, 64999, 65500, 0,
@@ -907,12 +827,8 @@ INSERT INTO commtoconf (commid, confid, sequence) VALUES (1, 1, 10);
INSERT INTO confalias (confid, alias) VALUES (1, 'Admin_Notes'); INSERT INTO confalias (confid, alias) VALUES (1, 'Admin_Notes');
INSERT INTO propconf (confid, ndx, data) VALUES (1, 0, ''); INSERT INTO propconf (confid, ndx, data) VALUES (1, 0, '');
# Make the Administrator the host-of-record of the "Administrative Notes" conference.
INSERT INTO confmember (confid, uid, granted_lvl) VALUES (1, 2, 52500); INSERT INTO confmember (confid, uid, granted_lvl) VALUES (1, 2, 52500);
# Add the 'Coffeehouse' community. This is the equivalent of the old CommunityWare
# 'Universal Community.'
# (COMMID = 2, CONTACTID = 4)
INSERT INTO communities (commid, createdate, read_lvl, write_lvl, create_lvl, delete_lvl, join_lvl, contactid, INSERT INTO communities (commid, createdate, read_lvl, write_lvl, create_lvl, delete_lvl, join_lvl, contactid,
host_uid, catid, membersonly, init_ftr, commname, language, synopsis, rules, alias) host_uid, catid, membersonly, init_ftr, commname, language, synopsis, rules, alias)
VALUES (2, '2000-12-01 00:00:00', 100, 58000, 58000, 65500, 500, 4, 2, 0, 1, 0, 'Coffeehouse', 'en-US', VALUES (2, '2000-12-01 00:00:00', 100, 58000, 58000, 65500, 500, 4, 2, 0, 1, 0, 'Coffeehouse', 'en-US',
@@ -922,22 +838,15 @@ INSERT INTO contacts (contactid, locality, country, owner_uid, owner_commid)
VALUES (4, 'Anywhere', 'US', 2, 2); VALUES (4, 'Anywhere', 'US', 2, 2);
INSERT INTO propcomm (cid, ndx, data) VALUES (2, 0, ''); INSERT INTO propcomm (cid, ndx, data) VALUES (2, 0, '');
# Insert the desired features for Coffeehouse.
INSERT INTO commftrs (commid, ftr_code) INSERT INTO commftrs (commid, ftr_code)
VALUES (2, 0), (2, 1), (2, 3), (2, 4); VALUES (2, 0), (2, 1), (2, 3), (2, 4);
# Make 'Anonymous Honyak' a member of Coffeehouse. This is important because new users will
# have the membership list of Anonymous Honyak copied to their account on signup (but with
# the 'member' access level).
INSERT INTO commmember (commid, uid, granted_lvl, locked, hidden) INSERT INTO commmember (commid, uid, granted_lvl, locked, hidden)
VALUES (2, 1, 100, 1, 1); VALUES (2, 1, 100, 1, 1);
# Make the 'Administrator' user the host of Coffeehouse.
INSERT INTO commmember (commid, uid, granted_lvl, locked) INSERT INTO commmember (commid, uid, granted_lvl, locked)
VALUES (2, 2, 58500, 1); VALUES (2, 2, 58500, 1);
# Insert the "General Discussion" conference into Coffeehouse.
# (CONFID = 2)
INSERT INTO confs (confid, createdate, read_lvl, post_lvl, create_lvl, hide_lvl, nuke_lvl, change_lvl, INSERT INTO confs (confid, createdate, read_lvl, post_lvl, create_lvl, hide_lvl, nuke_lvl, change_lvl,
delete_lvl, top_topic, name, descr) delete_lvl, top_topic, name, descr)
VALUES (2, '2000-12-01 00:00:00', 6500, 6500, 6500, 52500, 52500, 52500, 58000, 0, 'General Discussion', VALUES (2, '2000-12-01 00:00:00', 6500, 6500, 6500, 52500, 52500, 52500, 58000, 0, 'General Discussion',
@@ -946,11 +855,8 @@ INSERT INTO commtoconf (commid, confid, sequence) VALUES (2, 2, 10);
INSERT INTO confalias (confid, alias) VALUES (2, 'General'); INSERT INTO confalias (confid, alias) VALUES (2, 'General');
INSERT INTO propconf (confid, ndx, data) VALUES (2, 0, ''); INSERT INTO propconf (confid, ndx, data) VALUES (2, 0, '');
# Make the Administrator the host-of-record of the "General Discussion" conference.
INSERT INTO confmember (confid, uid, granted_lvl) VALUES (2, 2, 52500); INSERT INTO confmember (confid, uid, granted_lvl) VALUES (2, 2, 52500);
# Insert the "Test Postings" conference into Coffeehouse.
# (CONFID = 3)
INSERT INTO confs (confid, createdate, read_lvl, post_lvl, create_lvl, hide_lvl, nuke_lvl, change_lvl, INSERT INTO confs (confid, createdate, read_lvl, post_lvl, create_lvl, hide_lvl, nuke_lvl, change_lvl,
delete_lvl, top_topic, name, descr) delete_lvl, top_topic, name, descr)
VALUES (3, '2000-12-01 00:00:00', 6500, 6500, 6500, 52500, 52500, 52500, 58000, 0, 'Test Postings', VALUES (3, '2000-12-01 00:00:00', 6500, 6500, 6500, 52500, 52500, 52500, 58000, 0, 'Test Postings',
@@ -959,5 +865,4 @@ INSERT INTO commtoconf (commid, confid, sequence) VALUES (2, 3, 20);
INSERT INTO confalias (confid, alias) VALUES (3, 'Test'); INSERT INTO confalias (confid, alias) VALUES (3, 'Test');
INSERT INTO propconf (confid, ndx, data) VALUES (3, 0, ''); INSERT INTO propconf (confid, ndx, data) VALUES (3, 0, '');
# Make the Administrator the host-of-record of the "Test Postings" conference.
INSERT INTO confmember (confid, uid, granted_lvl) VALUES (3, 2, 52500); INSERT INTO confmember (confid, uid, granted_lvl) VALUES (3, 2, 52500);
+23
View File
@@ -0,0 +1,23 @@
# Amsterdam Web Communities System
# 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
# file, You can obtain one at https://mozilla.org/MPL/2.0/.
#
# SPDX-License-Identifier: MPL-2.0
#
CREATE TABLE newconfalias (
commid INT NOT NULL,
confid INT NOT NULL,
alias VARCHAR(64) NOT NULL,
PRIMARY KEY (commid, alias),
INDEX confid_x (commid, confid)
) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
INSERT INTO newconfalias (commid, confid, alias)
SELECT c.commid, c.confid, a.alias FROM commtoconf c, confalias a
WHERE c.confid = a.confid;
DROP TABLE confalias;
ALTER TABLE newconfalias RENAME TO confalias;
+2
View File
@@ -0,0 +1,2 @@
Migration scripts for MySQL go in this directory. Name them as YYYYMMDDNN.sql, where YYYYMMDD is the
current date and NN is a two-digit sequence number beginning at 01.
+24 -16
View File
@@ -253,7 +253,7 @@ func (p *PostHeader) Text(ctx context.Context) (string, error) {
} }
// Link returns a link string to this post. // Link returns a link string to this post.
func (p *PostHeader) Link(ctx context.Context, scope string) (string, error) { func (p *PostHeader) Link(ctx context.Context, commid int32, scope string) (string, error) {
if scope == "topic" { if scope == "topic" {
return fmt.Sprintf("%d", p.Num), nil return fmt.Sprintf("%d", p.Num), nil
} }
@@ -262,7 +262,7 @@ func (p *PostHeader) Link(ctx context.Context, scope string) (string, error) {
if err != nil { if err != nil {
return "", err return "", err
} }
parent, err := topic.Link(ctx, scope) parent, err := topic.Link(ctx, commid, scope)
if err != nil { if err != nil {
return "", err return "", err
} }
@@ -628,47 +628,55 @@ func AmNewPost(ctx context.Context, conf *Conference, topic *Topic, user *User,
* Array of post headers, or nil. * Array of post headers, or nil.
* Standard Go error status. * Standard Go error status.
*/ */
func AmGetPublishedPosts(ctx context.Context) ([]*PostHeader, error) { func AmGetPublishedPosts(ctx context.Context) ([]*PostHeader, []*Community, error) {
// Read the globals. // Read the globals.
gv, err := AmGlobals(ctx) gv, err := AmGlobals(ctx)
if err != nil { if err != nil {
return nil, err return nil, nil, err
} }
// Read the published posts. // Read the published posts.
rs, err := amdb.QueryContext(ctx, "SELECT postid FROM postpublish ORDER BY on_date DESC") rs, err := amdb.QueryContext(ctx, "SELECT commid, postid FROM postpublish ORDER BY on_date DESC")
if err != nil { if err != nil {
return nil, err return nil, nil, err
} }
// Extract post IDs to an array. // Extract post IDs to an array.
cids := make([]int32, gv.FrontPagePosts)
pids := make([]int64, gv.FrontPagePosts) pids := make([]int64, gv.FrontPagePosts)
i := 0 i := 0
for i < int(gv.FrontPagePosts) && rs.Next() { for i < int(gv.FrontPagePosts) && rs.Next() {
if err = rs.Scan(&(pids[i])); err != nil { if err = rs.Scan(&(cids[i]), &(pids[i])); err != nil {
return nil, err return nil, nil, err
} }
i++ i++
} }
if i == 0 { // no published posts, short-circuit response if i == 0 { // no published posts, short-circuit response
return make([]*PostHeader, 0), nil return make([]*PostHeader, 0), make([]*Community, 0), nil
} }
if i < int(gv.FrontPagePosts) { if i < int(gv.FrontPagePosts) {
cids = cids[:i]
pids = pids[:i] // truncate if we have fewer posts than spaces pids = pids[:i] // truncate if we have fewer posts than spaces
} }
// Build the communities return array.
comms := make([]*Community, len(cids))
for i, cid := range cids {
comms[i], _ = AmGetCommunity(ctx, cid)
}
// Use the post IDs to build a SQL statement. // Use the post IDs to build a SQL statement.
query, args, err := sqlx.In("SELECT * FROM posts WHERE postid IN (?)", pids) query, args, err := sqlx.In("SELECT * FROM posts WHERE postid IN (?)", pids)
if err != nil { if err != nil {
return nil, err return nil, nil, err
} }
query = amdb.Rebind(query) query = amdb.Rebind(query)
// Use the SQL to read in all the post headers using a single database query. // Use the SQL to read in all the post headers using a single database query.
var data []PostHeader var data []PostHeader
if err = amdb.SelectContext(ctx, &data, query, args...); err != nil { if err = amdb.SelectContext(ctx, &data, query, args...); err != nil {
return nil, err return nil, nil, err
} }
if len(data) < len(pids) { if len(data) < len(pids) {
return nil, errors.New("internal error reading post headers") return nil, nil, errors.New("internal error reading post headers")
} }
// Build the return array by making sure we point to the post headers in the same order the post IDs were returned. // Build the return array by making sure we point to the post headers in the same order the post IDs were returned.
@@ -683,10 +691,10 @@ func AmGetPublishedPosts(ctx context.Context) ([]*PostHeader, error) {
} }
} }
if q < len(pids) { if q < len(pids) {
return nil, errors.New("internal error generating output") return nil, nil, errors.New("internal error generating output")
} }
return rc, nil return rc, comms, nil
} }
type PostSearchResult struct { type PostSearchResult struct {
@@ -898,13 +906,13 @@ func AmSearchPosts(ctx context.Context, searchTerms string, u *User, offset, max
if err != nil { if err != nil {
return nil, count, err return nil, count, err
} }
alias, err := conf.Aliases(ctx) alias, err := conf.Aliases(ctx, commid)
if err != nil { if err != nil {
return nil, count, err return nil, count, err
} }
// Build the post link. // Build the post link.
plink := AmCreatePostLinkContext(commAlias, alias[0], topicNum) plink := AmCreatePostLinkContext(commAlias, commid, alias[0], topicNum)
plink.FirstPost = postnum plink.FirstPost = postnum
plink.LastPost = postnum plink.LastPost = postnum
rc[i].PostLink = plink.AsString() rc[i].PostLink = plink.AsString()
+6 -2
View File
@@ -23,6 +23,7 @@ import (
// PostLinkData is the structure holding the decoded parts of the post link. // PostLinkData is the structure holding the decoded parts of the post link.
type PostLinkData struct { type PostLinkData struct {
Community string Community string
CommId int32
Conference string Conference string
Topic int16 Topic int16
FirstPost int32 FirstPost int32
@@ -36,6 +37,7 @@ func (d *PostLinkData) NeedsDBVerification() bool {
// VerifyNames verifies the post link data against the database. // VerifyNames verifies the post link data against the database.
func (d *PostLinkData) VerifyNames(ctx context.Context) error { func (d *PostLinkData) VerifyNames(ctx context.Context) error {
commid := d.CommId
if d.Community != "" { if d.Community != "" {
comm, err := AmGetCommunityByAlias(ctx, d.Community) comm, err := AmGetCommunityByAlias(ctx, d.Community)
if err != nil { if err != nil {
@@ -44,9 +46,10 @@ func (d *PostLinkData) VerifyNames(ctx context.Context) error {
if comm == nil { if comm == nil {
return errors.New("community alias not found") return errors.New("community alias not found")
} }
commid = comm.Id
} }
if d.Conference != "" { if d.Conference != "" {
conf, err := AmGetConferenceByAlias(ctx, d.Conference) conf, err := AmGetConferenceByAlias(ctx, commid, d.Conference)
if err != nil { if err != nil {
return err return err
} }
@@ -392,9 +395,10 @@ func AmDecodePostLink(data string) (*PostLinkData, error) {
return &rc, nil return &rc, nil
} }
func AmCreatePostLinkContext(community string, conference string, topic int16) *PostLinkData { func AmCreatePostLinkContext(community string, commid int32, conference string, topic int16) *PostLinkData {
return &PostLinkData{ return &PostLinkData{
Community: community, Community: community,
CommId: commid,
Conference: conference, Conference: conference,
Topic: topic, Topic: topic,
FirstPost: -1, FirstPost: -1,
+2 -2
View File
@@ -43,7 +43,7 @@ type Topic struct {
} }
// Link returns a link string to this topic. // Link returns a link string to this topic.
func (t *Topic) Link(ctx context.Context, scope string) (string, error) { func (t *Topic) Link(ctx context.Context, commid int32, scope string) (string, error) {
if scope == "conference" { if scope == "conference" {
return fmt.Sprintf("%d.", t.Number), nil return fmt.Sprintf("%d.", t.Number), nil
} }
@@ -51,7 +51,7 @@ func (t *Topic) Link(ctx context.Context, scope string) (string, error) {
conf, err := AmGetConference(ctx, t.ConfId) conf, err := AmGetConference(ctx, t.ConfId)
if err == nil { if err == nil {
var plink string var plink string
plink, err = conf.Link(ctx, scope) plink, err = conf.Link(ctx, commid, scope)
if err == nil { if err == nil {
if strings.HasSuffix(plink, ".") { if strings.HasSuffix(plink, ".") {
return fmt.Sprintf("%s%d", plink, t.Number), nil return fmt.Sprintf("%s%d", plink, t.Number), nil
+12
View File
@@ -381,6 +381,18 @@ func (u *User) Prefs(ctx context.Context) (*UserPrefs, error) {
return u.prefs, nil return u.prefs, nil
} }
func (u *User) SetUsername(ctx context.Context, username string, setter *User, ipaddr string) error {
u.Mutex.Lock()
_, err := amdb.ExecContext(ctx, "UPDATE users SET username = ? WHERE uid = ?", username, u.Uid)
u.Mutex.Unlock()
if err == nil {
u.Username = username
AmStoreAudit(AmNewAudit(AuditAdminSetUserName, setter.Uid, ipaddr, fmt.Sprintf("uid=%d", u.Uid),
fmt.Sprintf("newname=%s", username)))
}
return err
}
/* SetProfileData sets the "profile" variables for this user. /* SetProfileData sets the "profile" variables for this user.
* Parameters: * Parameters:
* ctx - Standard Go context value. * ctx - Standard Go context value.
+26
View File
@@ -0,0 +1,26 @@
# Changelog
## Release 0.2.0 - April 29, 2026
### Bug Fixes
* Architectural error: conference aliases now have community scope, rather than global scope.
* Fix to link for showing hidden posts.
### Enhancements
* Added database initialization, conversion from Venice, and migration scripts.
* Admin can now change the name of a user account (issue #2).
* Apple site icon added (issue #5).
### Meta-Enhancements
* Action included to automagically build binaries when a release happens.
## Release 0.1.1 - April 11, 2020
* Fixed a bug in post link resolution at post time which was causing the conference alias to be set incorrectly (issue #3).
## Release 0.1.0 - April 6, 2026
* Implemented all functionality from the last known version of the Venice Web Communities System, circa 2006.
+1 -1
View File
@@ -88,7 +88,7 @@ func AmDeliverSubscription(ctx context.Context, comm *database.Community, conf *
vars.Set("communityName", comm.Name) vars.Set("communityName", comm.Name)
vars.Set("conferenceName", conf.Name) vars.Set("conferenceName", conf.Name)
vars.Set("topicName", topic.Name) vars.Set("topicName", topic.Name)
pl := database.AmCreatePostLinkContext(comm.Alias, confAlias, topic.Number) pl := database.AmCreatePostLinkContext(comm.Alias, comm.Id, confAlias, topic.Number)
vars.Set("topicLink", pl.AsString()) vars.Set("topicLink", pl.AsString())
vars.Set("pseud", realPseud) vars.Set("pseud", realPseud)
vars.Set("text", realText) vars.Set("text", realText)
+8 -10
View File
@@ -345,12 +345,11 @@ func FindPostsPageCommunity(ctxt ui.AmContext) (string, any) {
* Data as a parameter for the command string. * Data as a parameter for the command string.
*/ */
func FindPostsPageConference(ctxt ui.AmContext) (string, any) { func FindPostsPageConference(ctxt ui.AmContext) (string, any) {
comm := ctxt.CurrentCommunity()
conf := ctxt.GetScratch("currentConference").(*database.Conference) conf := ctxt.GetScratch("currentConference").(*database.Conference)
ctxt.VarMap().Set("scope", "conference") ctxt.VarMap().Set("scope", "conference")
ctxt.VarMap().Set("entityName", conf.Name) ctxt.VarMap().Set("entityName", conf.Name)
ctxt.VarMap().Set("backlink", fmt.Sprintf("/comm/%s/conf/%s", comm.Alias, ctxt.GetScratch("currentAlias"))) ctxt.VarMap().Set("backlink", ctxt.GetScratch("ConferenceLink").(string))
ctxt.VarMap().Set("postlink", fmt.Sprintf("/comm/%s/conf/%s/find", comm.Alias, ctxt.GetScratch("currentAlias"))) ctxt.VarMap().Set("postlink", fmt.Sprintf("%s/find", ctxt.GetScratch("ConferenceLink")))
return commonFindGetBackend(ctxt) return commonFindGetBackend(ctxt)
} }
@@ -362,12 +361,11 @@ func FindPostsPageConference(ctxt ui.AmContext) (string, any) {
* Data as a parameter for the command string. * Data as a parameter for the command string.
*/ */
func FindPostsPageTopic(ctxt ui.AmContext) (string, any) { func FindPostsPageTopic(ctxt ui.AmContext) (string, any) {
comm := ctxt.CurrentCommunity()
topic := ctxt.GetScratch("currentTopic").(*database.Topic) topic := ctxt.GetScratch("currentTopic").(*database.Topic)
ctxt.VarMap().Set("scope", "topic") ctxt.VarMap().Set("scope", "topic")
ctxt.VarMap().Set("entityName", topic.Name) ctxt.VarMap().Set("entityName", topic.Name)
ctxt.VarMap().Set("backlink", fmt.Sprintf("/comm/%s/conf/%s/r/%d", comm.Alias, ctxt.GetScratch("currentAlias"), topic.Number)) ctxt.VarMap().Set("backlink", fmt.Sprintf("%s/r/%d", ctxt.GetScratch("ConferenceLink"), topic.Number))
ctxt.VarMap().Set("postlink", fmt.Sprintf("/comm/%s/conf/%s/op/%d/find", comm.Alias, ctxt.GetScratch("currentAlias"), topic.Number)) ctxt.VarMap().Set("postlink", fmt.Sprintf("%s/op/%d/find", ctxt.GetScratch("ConferenceLink"), topic.Number))
return commonFindGetBackend(ctxt) return commonFindGetBackend(ctxt)
} }
@@ -438,8 +436,8 @@ func FindPostsConference(ctxt ui.AmContext) (string, any) {
conf := ctxt.GetScratch("currentConference").(*database.Conference) conf := ctxt.GetScratch("currentConference").(*database.Conference)
ctxt.VarMap().Set("scope", "conference") ctxt.VarMap().Set("scope", "conference")
ctxt.VarMap().Set("entityName", conf.Name) ctxt.VarMap().Set("entityName", conf.Name)
ctxt.VarMap().Set("backlink", fmt.Sprintf("/comm/%s/conf/%s", comm.Alias, ctxt.GetScratch("currentAlias"))) ctxt.VarMap().Set("backlink", ctxt.GetScratch("ConferenceLink").(string))
ctxt.VarMap().Set("postlink", fmt.Sprintf("/comm/%s/conf/%s/find", comm.Alias, ctxt.GetScratch("currentAlias"))) ctxt.VarMap().Set("postlink", fmt.Sprintf("%s/find", ctxt.GetScratch("ConferenceLink")))
return commonFindPostBackend(ctxt, comm, conf, nil) return commonFindPostBackend(ctxt, comm, conf, nil)
} }
@@ -456,7 +454,7 @@ func FindPostsTopic(ctxt ui.AmContext) (string, any) {
topic := ctxt.GetScratch("currentTopic").(*database.Topic) topic := ctxt.GetScratch("currentTopic").(*database.Topic)
ctxt.VarMap().Set("scope", "topic") ctxt.VarMap().Set("scope", "topic")
ctxt.VarMap().Set("entityName", topic.Name) ctxt.VarMap().Set("entityName", topic.Name)
ctxt.VarMap().Set("backlink", fmt.Sprintf("/comm/%s/conf/%s/r/%d", comm.Alias, ctxt.GetScratch("currentAlias"), topic.Number)) ctxt.VarMap().Set("backlink", fmt.Sprintf("%s/r/%d", ctxt.GetScratch("ConferenceLink"), topic.Number))
ctxt.VarMap().Set("postlink", fmt.Sprintf("/comm/%s/conf/%s/op/%d/find", comm.Alias, ctxt.GetScratch("currentAlias"), topic.Number)) ctxt.VarMap().Set("postlink", fmt.Sprintf("%s/op/%d/find", ctxt.GetScratch("ConferenceLink"), topic.Number))
return commonFindPostBackend(ctxt, comm, conf, topic) return commonFindPostBackend(ctxt, comm, conf, topic)
} }
+1 -40
View File
@@ -1,28 +1,19 @@
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
filippo.io/edwards25519 v1.2.0 h1:crnVqOiS4jqYleHd9vaKZ+HKtHfllngJIiOpNpoJsjo= filippo.io/edwards25519 v1.2.0 h1:crnVqOiS4jqYleHd9vaKZ+HKtHfllngJIiOpNpoJsjo=
filippo.io/edwards25519 v1.2.0/go.mod h1:xzAOLCNug/yB62zG1bQ8uziwrIqIuxhctzJT18Q77mc= filippo.io/edwards25519 v1.2.0/go.mod h1:xzAOLCNug/yB62zG1bQ8uziwrIqIuxhctzJT18Q77mc=
github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 h1:sR+/8Yb4slttB4vD+b9btVEnWgL3Q00OBTzVT8B9C0c=
github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno= github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno=
github.com/CloudyKit/fastprinter v0.0.0-20251202014920-1725d2651bd4 h1:DQ1+lDdBve+u+aovjh4wV6sYnvZKH0Hx8GaQOi4vYl8= github.com/CloudyKit/fastprinter v0.0.0-20251202014920-1725d2651bd4 h1:DQ1+lDdBve+u+aovjh4wV6sYnvZKH0Hx8GaQOi4vYl8=
github.com/CloudyKit/fastprinter v0.0.0-20251202014920-1725d2651bd4/go.mod h1:eauGmjfZG874MOAEPVeqg21mZCbTOLW+tFe8F7NpfnY= github.com/CloudyKit/fastprinter v0.0.0-20251202014920-1725d2651bd4/go.mod h1:eauGmjfZG874MOAEPVeqg21mZCbTOLW+tFe8F7NpfnY=
github.com/CloudyKit/jet/v6 v6.3.1 h1:6IAo5Cx21xrHVaR8zzXN5gJatKV/wO7Nf6bfCnCSbUw=
github.com/CloudyKit/jet/v6 v6.3.1/go.mod h1:lf8ksdNsxZt7/yH/3n4vJQWA9RUq4wpaHtArHhGVMOw=
github.com/CloudyKit/jet/v6 v6.3.2 h1:BPaX0lnXTZ9TniICiiK/0iJqzeGJ2ibvB4DjAqLMBSM= github.com/CloudyKit/jet/v6 v6.3.2 h1:BPaX0lnXTZ9TniICiiK/0iJqzeGJ2ibvB4DjAqLMBSM=
github.com/CloudyKit/jet/v6 v6.3.2/go.mod h1:lf8ksdNsxZt7/yH/3n4vJQWA9RUq4wpaHtArHhGVMOw= github.com/CloudyKit/jet/v6 v6.3.2/go.mod h1:lf8ksdNsxZt7/yH/3n4vJQWA9RUq4wpaHtArHhGVMOw=
github.com/alexflint/go-arg v1.6.0 h1:wPP9TwTPO54fUVQl4nZoxbFfKCcy5E6HBCumj1XVRSo=
github.com/alexflint/go-arg v1.6.0/go.mod h1:A7vTJzvjoaSTypg4biM5uYNTkJ27SkNTArtYXnlqVO8=
github.com/alexflint/go-arg v1.6.1 h1:uZogJ6VDBjcuosydKgvYYRhh9sRCusjOvoOLZopBlnA= github.com/alexflint/go-arg v1.6.1 h1:uZogJ6VDBjcuosydKgvYYRhh9sRCusjOvoOLZopBlnA=
github.com/alexflint/go-arg v1.6.1/go.mod h1:nQ0LFYftLJ6njcaee0sU+G0iS2+2XJQfA8I062D0LGc= github.com/alexflint/go-arg v1.6.1/go.mod h1:nQ0LFYftLJ6njcaee0sU+G0iS2+2XJQfA8I062D0LGc=
github.com/alexflint/go-scalar v1.2.0 h1:WR7JPKkeNpnYIOfHRa7ivM21aWAdHD0gEWHCx+WQBRw= github.com/alexflint/go-scalar v1.2.0 h1:WR7JPKkeNpnYIOfHRa7ivM21aWAdHD0gEWHCx+WQBRw=
github.com/alexflint/go-scalar v1.2.0/go.mod h1:LoFvNMqS1CPrMVltza4LvnGKhaSpc3oyLEBUZVhhS2o= github.com/alexflint/go-scalar v1.2.0/go.mod h1:LoFvNMqS1CPrMVltza4LvnGKhaSpc3oyLEBUZVhhS2o=
github.com/biter777/countries v1.7.5 h1:MJ+n3+rSxWQdqVJU8eBy9RqcdH6ePPn4PJHocVWUa+Q= github.com/biter777/countries v1.7.5 h1:MJ+n3+rSxWQdqVJU8eBy9RqcdH6ePPn4PJHocVWUa+Q=
github.com/biter777/countries v1.7.5/go.mod h1:1HSpZ526mYqKJcpT5Ti1kcGQ0L0SrXWIaptUWjFfv2E= github.com/biter777/countries v1.7.5/go.mod h1:1HSpZ526mYqKJcpT5Ti1kcGQ0L0SrXWIaptUWjFfv2E=
github.com/bits-and-blooms/bitset v1.24.0 h1:H4x4TuulnokZKvHLfzVRTHJfFfnHEeSYJizujEZvmAM=
github.com/bits-and-blooms/bitset v1.24.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
github.com/bits-and-blooms/bitset v1.24.4 h1:95H15Og1clikBrKr/DuzMXkQzECs1M6hhoGXLwLQOZE= github.com/bits-and-blooms/bitset v1.24.4 h1:95H15Og1clikBrKr/DuzMXkQzECs1M6hhoGXLwLQOZE=
github.com/bits-and-blooms/bitset v1.24.4/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= github.com/bits-and-blooms/bitset v1.24.4/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/derekparker/trie v0.0.0-20230829180723-39f4de51ef7d h1:hUWoLdw5kvo2xCsqlsIBMvWUc1QCSsCYD2J2+Fg6YoU= github.com/derekparker/trie v0.0.0-20230829180723-39f4de51ef7d h1:hUWoLdw5kvo2xCsqlsIBMvWUc1QCSsCYD2J2+Fg6YoU=
@@ -40,8 +31,6 @@ github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o=
github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY= github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY=
github.com/klauspost/lctime v0.1.0 h1:nINsuFc860M9cyYhT6vfg6U1USh7kiVBj/s/2b04U70= github.com/klauspost/lctime v0.1.0 h1:nINsuFc860M9cyYhT6vfg6U1USh7kiVBj/s/2b04U70=
github.com/klauspost/lctime v0.1.0/go.mod h1:OwdMhr8tbQvusAsnilqkkgDQqivWlqyg0w5cfXkLiDk= github.com/klauspost/lctime v0.1.0/go.mod h1:OwdMhr8tbQvusAsnilqkkgDQqivWlqyg0w5cfXkLiDk=
github.com/labstack/echo/v4 v4.13.4 h1:oTZZW+T3s9gAu5L8vmzihV7/lkXGZuITzTQkTEhcXEA=
github.com/labstack/echo/v4 v4.13.4/go.mod h1:g63b33BZ5vZzcIUF8AtRH40DrTlXnx4UMC8rBdndmjQ=
github.com/labstack/echo/v4 v4.15.1 h1:S9keusg26gZpjMmPqB5hOEvNKnmd1lNmcHrbbH2lnFs= github.com/labstack/echo/v4 v4.15.1 h1:S9keusg26gZpjMmPqB5hOEvNKnmd1lNmcHrbbH2lnFs=
github.com/labstack/echo/v4 v4.15.1/go.mod h1:xmw1clThob0BSVRX1CRQkGQ/vjwcpOMjQZSZa9fKA/c= github.com/labstack/echo/v4 v4.15.1/go.mod h1:xmw1clThob0BSVRX1CRQkGQ/vjwcpOMjQZSZa9fKA/c=
github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0= github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0=
@@ -56,61 +45,33 @@ github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w= github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w=
github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g= github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/tkuchiki/go-timezone v0.2.3 h1:D3TVdIPrFsu9lxGxqNX2wsZwn1MZtTqTW0mdevMozHc= github.com/tkuchiki/go-timezone v0.2.3 h1:D3TVdIPrFsu9lxGxqNX2wsZwn1MZtTqTW0mdevMozHc=
github.com/tkuchiki/go-timezone v0.2.3/go.mod h1:oFweWxYl35C/s7HMVZXiA19Jr9Y0qJHMaG/J2TES4LY= github.com/tkuchiki/go-timezone v0.2.3/go.mod h1:oFweWxYl35C/s7HMVZXiA19Jr9Y0qJHMaG/J2TES4LY=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo= github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo=
github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8=
golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw=
golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts=
golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos=
golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4= golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4=
golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA= golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA=
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8 h1:hVwzHzIUGRjiF7EcUjqNxk3NCfkPxbDKRdnNE1Rpg0U=
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.37.0 h1:ZiRjArKI8GwxZOoEtUfhrBtaCN+4b/7709dlT6SSnQA= golang.org/x/image v0.37.0 h1:ZiRjArKI8GwxZOoEtUfhrBtaCN+4b/7709dlT6SSnQA=
golang.org/x/image v0.37.0/go.mod h1:/3f6vaXC+6CEanU4KJxbcUZyEePbyKbaLoDOe4ehFYY= golang.org/x/image v0.37.0/go.mod h1:/3f6vaXC+6CEanU4KJxbcUZyEePbyKbaLoDOe4ehFYY=
golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY=
golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
golang.org/x/net v0.50.0 h1:ucWh9eiCGyDR3vtzso0WMQinm2Dnt8cFMuQa9K33J60=
golang.org/x/net v0.50.0/go.mod h1:UgoSli3F/pBgdJBHCTc+tp3gmrU4XswgGRgtnwWTfyM=
golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0= golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0=
golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw= golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=
golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo= golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k=
golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM=
golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk=
golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA=
golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8= golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8=
golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA= golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA=
golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0=
golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=
golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=
golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U=
golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+2
View File
@@ -127,6 +127,7 @@ func buildPostLink(decoded, context *database.PostLinkData) string {
b.WriteString(context.Conference) b.WriteString(context.Conference)
} else { } else {
b.WriteString(decoded.Conference) b.WriteString(decoded.Conference)
started = true
} }
b.WriteString(".") b.WriteString(".")
if decoded.Topic == -1 { if decoded.Topic == -1 {
@@ -168,6 +169,7 @@ func (rw *postLinkRewriter) Rewrite(ctx context.Context, data string, svc rewrit
if err != nil { if err != nil {
return nil return nil
} }
mydata.CommId = ctxt.CommId
err = mydata.VerifyNames(ctx) err = mydata.VerifyNames(ctx)
if err != nil { if err != nil {
return nil return nil
+2 -2
View File
@@ -59,7 +59,7 @@ func InviteToConference(ctxt ui.AmContext) (string, any) {
ctxt.SetFrameTitle("Send Invitation") ctxt.SetFrameTitle("Send Invitation")
ctxt.VarMap().Set("title", "Send Conference Invitation") ctxt.VarMap().Set("title", "Send Conference Invitation")
ctxt.VarMap().Set("subtitle", conf.Name) ctxt.VarMap().Set("subtitle", conf.Name)
ctxt.VarMap().Set("backlink", fmt.Sprintf("/comm/%s/conf/%s/manage", comm.Alias, ctxt.GetScratch("currentAlias"))) ctxt.VarMap().Set("backlink", fmt.Sprintf("%s/manage", ctxt.GetScratch("ConferenceLink")))
ctxt.VarMap().Set("cid", fmt.Sprintf("%d", comm.Id)) ctxt.VarMap().Set("cid", fmt.Sprintf("%d", comm.Id))
ctxt.VarMap().Set("confid", fmt.Sprintf("%d", conf.ConfId)) ctxt.VarMap().Set("confid", fmt.Sprintf("%d", conf.ConfId))
return "framed", "invite.jet" return "framed", "invite.jet"
@@ -83,7 +83,7 @@ func InviteToTopic(ctxt ui.AmContext) (string, any) {
ctxt.SetFrameTitle("Send Invitation") ctxt.SetFrameTitle("Send Invitation")
ctxt.VarMap().Set("title", "Send Topic Invitation") ctxt.VarMap().Set("title", "Send Topic Invitation")
ctxt.VarMap().Set("subtitle", topic.Name) ctxt.VarMap().Set("subtitle", topic.Name)
ctxt.VarMap().Set("backlink", fmt.Sprintf("/comm/%s/conf/%s/op/%d/manage", comm.Alias, ctxt.GetScratch("currentAlias"), topic.Number)) ctxt.VarMap().Set("backlink", fmt.Sprintf("%s/op/%d/manage", ctxt.GetScratch("ConferenceLink"), topic.Number))
ctxt.VarMap().Set("cid", fmt.Sprintf("%d", comm.Id)) ctxt.VarMap().Set("cid", fmt.Sprintf("%d", comm.Id))
ctxt.VarMap().Set("confid", fmt.Sprintf("%d", conf.ConfId)) ctxt.VarMap().Set("confid", fmt.Sprintf("%d", conf.ConfId))
ctxt.VarMap().Set("topicid", fmt.Sprintf("%d", topic.TopicId)) ctxt.VarMap().Set("topicid", fmt.Sprintf("%d", topic.TopicId))
+10
View File
@@ -249,6 +249,7 @@ func UserManagementForm(ctxt ui.AmContext) (string, any) {
var prefs *database.UserPrefs var prefs *database.UserPrefs
prefs, err = user.Prefs(ctxt.Ctx()) prefs, err = user.Prefs(ctxt.Ctx())
if err == nil { if err == nil {
dlg.Field("user").Value = user.Username
dlg.Field("remind").Value = user.PassReminder dlg.Field("remind").Value = user.PassReminder
dlg.Field("base_lvl").SetLevel(user.BaseLevel) dlg.Field("base_lvl").SetLevel(user.BaseLevel)
dlg.Field("verify_email").SetChecked(user.VerifyEMail) dlg.Field("verify_email").SetChecked(user.VerifyEMail)
@@ -327,6 +328,12 @@ func UserManagementSave(ctxt ui.AmContext) (string, any) {
if err == nil { if err == nil {
var prefs *database.UserPrefs var prefs *database.UserPrefs
prefs, err = user.Prefs(ctxt.Ctx()) prefs, err = user.Prefs(ctxt.Ctx())
if err == nil && user.Username != dlg.Field("user").Value {
u2, e := database.AmGetUserByName(ctxt.Ctx(), dlg.Field("user").Value, nil)
if e == nil && u2 != nil {
err = errors.New("user name is already in use")
}
}
if err == nil && !(dlg.Field("pass1").IsEmpty() && dlg.Field("pass2").IsEmpty()) { if err == nil && !(dlg.Field("pass1").IsEmpty() && dlg.Field("pass2").IsEmpty()) {
p1 := dlg.Field("pass1").Value p1 := dlg.Field("pass1").Value
if p1 == dlg.Field("pass2").Value { if p1 == dlg.Field("pass2").Value {
@@ -378,6 +385,9 @@ func UserManagementSave(ctxt ui.AmContext) (string, any) {
err = user.SaveFlags(ctxt.Ctx(), nf) err = user.SaveFlags(ctxt.Ctx(), nf)
} }
} }
if err == nil && user.Username != dlg.Field("user").Value {
err = user.SetUsername(ctxt.Ctx(), dlg.Field("user").Value, ctxt.CurrentUser(), ctxt.RemoteIP())
}
if err == nil { if err == nil {
err = user.SetProfileData(ctxt.Ctx(), dlg.Field("remind").Value, dlg.Field("dob").AsDate(), dlg.Field("descr").ValPtr(), err = user.SetProfileData(ctxt.Ctx(), dlg.Field("remind").Value, dlg.Field("dob").AsDate(), dlg.Field("descr").ValPtr(),
ctxt.CurrentUser(), ctxt.RemoteIP()) ctxt.CurrentUser(), ctxt.RemoteIP())
+7 -5
View File
@@ -93,7 +93,7 @@ func renderSBConferences(ctx context.Context, u *database.User, sb *DisplaySideb
return err return err
} }
var a []string var a []string
if a, err = conf[i].Aliases(ctx); err != nil { if a, err = conf[i].Aliases(ctx, comm[i].Id); err != nil {
return err return err
} }
alias[i] = a[0] alias[i] = a[0]
@@ -176,9 +176,10 @@ func templateGetTopic(args jet.Arguments) reflect.Value {
// templateTopicLink returns the link string for the given topic. // templateTopicLink returns the link string for the given topic.
func templateTopicLink(args jet.Arguments) reflect.Value { func templateTopicLink(args jet.Arguments) reflect.Value {
topic := args.Get(0).Convert(reflect.TypeFor[*database.Topic]()).Interface().(*database.Topic) comm := args.Get(0).Convert(reflect.TypeFor[*database.Community]()).Interface().(*database.Community)
ctxt := args.Get(1).Convert(reflect.TypeFor[ui.AmContext]()).Interface().(ui.AmContext) topic := args.Get(1).Convert(reflect.TypeFor[*database.Topic]()).Interface().(*database.Topic)
link, _ := topic.Link(ctxt.Ctx(), "global") ctxt := args.Get(2).Convert(reflect.TypeFor[ui.AmContext]()).Interface().(ui.AmContext)
link, _ := topic.Link(ctxt.Ctx(), comm.Id, "global")
return reflect.ValueOf(link) return reflect.ValueOf(link)
} }
@@ -194,12 +195,13 @@ func TopPage(ctxt ui.AmContext) (string, any) {
ctxt.SetFrameTitle("My Front Page") ctxt.SetFrameTitle("My Front Page")
// Retrieve the published posts. // Retrieve the published posts.
hdrs, err := database.AmGetPublishedPosts(ctxt.Ctx()) hdrs, comms, err := database.AmGetPublishedPosts(ctxt.Ctx())
if err != nil { if err != nil {
return "error", err return "error", err
} }
ctxt.VarMap().Set("posts", hdrs) ctxt.VarMap().Set("posts", hdrs)
ctxt.VarMap().Set("comms", comms)
ctxt.VarMap().SetFunc("post_getText", templatePostText) ctxt.VarMap().SetFunc("post_getText", templatePostText)
ctxt.VarMap().SetFunc("post_getUserName", templateExtractUserName) ctxt.VarMap().SetFunc("post_getUserName", templateExtractUserName)
ctxt.VarMap().SetFunc("post_topic", templateGetTopic) ctxt.VarMap().SetFunc("post_topic", templateGetTopic)
+3 -2
View File
@@ -342,7 +342,6 @@ func (st *amSessionStore) SessionInfo() (int, []string, int) {
func (st *amSessionStore) sweep(tick <-chan time.Time, done chan bool) { func (st *amSessionStore) sweep(tick <-chan time.Time, done chan bool) {
for range tick { for range tick {
if st.sweepRunning.Load() { if st.sweepRunning.Load() {
log.Infof("session sweep running")
// phase 1 - identify expired sessions // phase 1 - identify expired sessions
st.mutex.RLock() st.mutex.RLock()
zap := make([]string, 0, len(st.sessions)) zap := make([]string, 0, len(st.sessions))
@@ -353,7 +352,9 @@ func (st *amSessionStore) sweep(tick <-chan time.Time, done chan bool) {
} }
} }
st.mutex.RUnlock() st.mutex.RUnlock()
log.Infof("identified %d sessions to zap", len(zap)) if len(zap) > 0 {
log.Infof("identified %d sessions to zap", len(zap))
}
// phase 2 - get rid of the expired sessions // phase 2 - get rid of the expired sessions
for _, k := range zap { for _, k := range zap {
+9
View File
@@ -15,6 +15,15 @@ title: "Modify User Account"
subtitle: "User: [USERNAME]" subtitle: "User: [USERNAME]"
action: "/sysadmin/users/[USERNAME]" action: "/sysadmin/users/[USERNAME]"
fields: fields:
- type: "header"
name: "header0"
caption: "User Information"
- type: "ams_id"
name: "user"
caption: "User Name"
required: true
size: 32
maxlength: 64
- type: "header" - type: "header"
name: "header1" name: "header1"
caption: "Security Information" caption: "Security Information"
+11 -1
View File
@@ -17,6 +17,7 @@ import (
"net/http" "net/http"
"net/url" "net/url"
"strconv" "strconv"
"strings"
"git.erbosoft.com/amy/amsterdam/config" "git.erbosoft.com/amy/amsterdam/config"
"git.erbosoft.com/amy/amsterdam/database" "git.erbosoft.com/amy/amsterdam/database"
@@ -82,6 +83,10 @@ func SetCommunity(next echo.HandlerFunc) echo.HandlerFunc {
if err != nil { if err != nil {
return AmSendPageData(c, ctxt, "error", echo.NewHTTPError(http.StatusNotFound).SetInternal(err)) return AmSendPageData(c, ctxt, "error", echo.NewHTTPError(http.StatusNotFound).SetInternal(err))
} }
var b strings.Builder
b.WriteString("/comm/")
b.WriteString(ctxt.CurrentCommunity().Alias)
ctxt.SetScratch("CommunityLink", b.String())
ctxt.SetLeftMenu("community") ctxt.SetLeftMenu("community")
return next(c) return next(c)
} }
@@ -113,7 +118,7 @@ func ValidateConference(next echo.HandlerFunc) echo.HandlerFunc {
func SetConference(next echo.HandlerFunc) echo.HandlerFunc { func SetConference(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error { return func(c echo.Context) error {
ctxt := AmContextFromEchoContext(c) ctxt := AmContextFromEchoContext(c)
conf, err := database.AmGetConferenceByAliasInCommunity(ctxt.Ctx(), ctxt.CurrentCommunity().Id, ctxt.URLParam("confid")) conf, err := database.AmGetConferenceByAlias(ctxt.Ctx(), ctxt.CurrentCommunity().Id, ctxt.URLParam("confid"))
if err != nil { if err != nil {
return AmSendPageData(c, ctxt, "error", err) return AmSendPageData(c, ctxt, "error", err)
} }
@@ -128,6 +133,11 @@ func SetConference(next echo.HandlerFunc) echo.HandlerFunc {
ctxt.SetScratch("currentConference", conf) ctxt.SetScratch("currentConference", conf)
ctxt.SetScratch("currentAlias", ctxt.URLParam("confid")) ctxt.SetScratch("currentAlias", ctxt.URLParam("confid"))
ctxt.SetScratch("levelInConference", myLevel) ctxt.SetScratch("levelInConference", myLevel)
var b strings.Builder
b.WriteString(ctxt.GetScratch("CommunityLink").(string))
b.WriteString("/conf/")
b.WriteString(ctxt.URLParam("confid"))
ctxt.SetScratch("ConferenceLink", b.String())
return next(c) return next(c)
} }
} }
Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

+1
View File
@@ -17,6 +17,7 @@
<title>{{ .FrameTitle() | raw }} - {{ GlobalConfig.Site.Title }}</title> <title>{{ .FrameTitle() | raw }} - {{ GlobalConfig.Site.Title }}</title>
<link rel="icon" href="{{ GlobalConfig.Site.SiteIcon.Path }}" type="{{ GlobalConfig.Site.SiteIcon.Type }}" /> <link rel="icon" href="{{ GlobalConfig.Site.SiteIcon.Path }}" type="{{ GlobalConfig.Site.SiteIcon.Type }}" />
<link rel="shortcut icon" href="{{ GlobalConfig.Site.SiteShortcutIcon }}" /> <link rel="shortcut icon" href="{{ GlobalConfig.Site.SiteShortcutIcon }}" />
<link rel="apple-touch-icon" href="{{ GlobalConfig.Site.SiteAppleIcon }}" />
{{ range k, v := .FrameMetadata(0) }} {{ range k, v := .FrameMetadata(0) }}
<meta http-equiv="{{ k }}" content="{{ v }}"> <meta http-equiv="{{ k }}" content="{{ v }}">
{{ end }} {{ end }}
+2 -2
View File
@@ -117,8 +117,8 @@
{{ post_cur = p }} {{ post_cur = p }}
{{ post_userName = post_getUserName(p, .) }} {{ post_userName = post_getUserName(p, .) }}
{{ post_text = post_getText(p, .) }} {{ post_text = post_getText(p, .) }}
{{ post_overrideLine = post_getOverrideLine(p, .) }} {{ post_overrideLine = post_getOverrideLine(p, advancedControls, .) }}
{{ post_overrideLink = post_getOverrideLink(p, post_topicPermalink) }} {{ post_overrideLink = post_getOverrideLink(p, advancedControls, post_topicLink) }}
{{ post_attach = post_getAttachmentInfo(p, .) }} {{ post_attach = post_getAttachmentInfo(p, .) }}
{{ post_bozo = post_isBozo(p, post_topic, .) }} {{ post_bozo = post_isBozo(p, post_topic, .) }}
{{ if showPics }} {{ if showPics }}
+2 -2
View File
@@ -32,8 +32,8 @@
{{ post_cur = p }} {{ post_cur = p }}
{{ post_userName = post_getUserName(p, .) }} {{ post_userName = post_getUserName(p, .) }}
{{ post_text = post_getText(p, .) }} {{ post_text = post_getText(p, .) }}
{{ post_overrideLine = post_getOverrideLine(p, .) }} {{ post_overrideLine = post_getOverrideLine(p, true, .) }}
{{ post_overrideLink = post_getOverrideLink(p, post_topicPermalink) }} {{ post_overrideLink = post_getOverrideLink(p, true, post_topicLink) }}
{{ post_attach = post_getAttachmentInfo(p, .) }} {{ post_attach = post_getAttachmentInfo(p, .) }}
{{ post_bozo = post_isBozo(p, post_topic, .) }} {{ post_bozo = post_isBozo(p, post_topic, .) }}
{{ include "singlepost.jet" }} {{ include "singlepost.jet" }}
+1 -1
View File
@@ -33,7 +33,7 @@
{{ user = post_getUserName(p, .) }} {{ user = post_getUserName(p, .) }}
{{ text = post_getText(p, .) }} {{ text = post_getText(p, .) }}
{{ topic = post_topic(p, .) }} {{ topic = post_topic(p, .) }}
{{ link = post_topicLink(topic, .) }} {{ link = post_topicLink(comms[i], topic, .) }}
<div class="text-black text-sm"> <div class="text-black text-sm">
<div class="mb-2"> <div class="mb-2">
<strong>{{ p.Pseud | raw }}</strong> <strong>{{ p.Pseud | raw }}</strong>