14 Commits

33 changed files with 366 additions and 270 deletions
+67
View File
@@ -0,0 +1,67 @@
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 Linux AMD64 Binary
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ github.event.release.upload_url }}
asset_path: ./amsterdam-linux-amd64
asset_name: amsterdam-linux-amd64
asset_content_type: application/octet-stream
- name: Upload Linux ARM64 Binary
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ github.event.release.upload_url }}
asset_path: ./amsterdam-linux-arm64
asset_name: amsterdam-linux-arm64
asset_content_type: application/octet-stream
- name: Upload Windows Binary
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ github.event.release.upload_url }}
asset_path: ./amsterdam-windows-amd64.exe
asset_name: amsterdam-windows-amd64.exe
asset_content_type: application/vnd.microsoft.portable-executable
- name: Upload Mac ARM64 Binary
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ github.event.release.upload_url }}
asset_path: ./amsterdam-macos-arm64
asset_name: amsterdam-macos-arm64
asset_content_type: application/octet-stream
- name: Upload Mac AMD64 Binary
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ github.event.release.upload_url }}
asset_path: ./amsterdam-macos-amd64
asset_name: amsterdam-macos-amd64
asset_content_type: application/octet-stream
+1 -1
View File
@@ -6,4 +6,4 @@ __debug_bin*
/test*.yaml
# Ignore mac-specific files
/.DS_Store
.DS_Store
+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
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`
(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.
Ensure a user in your database is granted all privileges to all tables in your new database.
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:
@@ -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).
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,
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:
+4 -19
View File
@@ -1,22 +1,10 @@
# 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
* 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.
* Database format migrations.
* <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.</s>
* ~~Database format migrations.~~
* Allow use of Postgres as a database.
* Dockerization.
* 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?
* 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
* 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.
* Fix password storage. Straight SHA-1 hashes aren't gonna cut it. There are better ways.
* 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
urlStem := fmt.Sprintf("/comm/%s/conf/%s", comm.Alias, ctxt.GetScratch("currentAlias"))
urlStem := ctxt.GetScratch("ConferenceLink").(string)
if !ctxt.CurrentUser().IsAnon {
traverser := ui.NewTopicTraverser(topics)
ctxt.SetSession("topic.traverser", traverser)
@@ -178,14 +178,13 @@ func Topics(ctxt ui.AmContext) (string, any) {
* Data as a parameter for the command string.
*/
func NewTopicForm(ctxt ui.AmContext) (string, any) {
comm := ctxt.CurrentCommunity()
conf := ctxt.GetScratch("currentConference").(*database.Conference)
myLevel := ctxt.GetScratch("levelInConference").(uint16)
if !conf.TestPermission("Conference.Create", myLevel) {
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("urlStem", fmt.Sprintf("/comm/%s/conf/%s", comm.Alias, ctxt.GetScratch("currentAlias")))
ctxt.VarMap().Set("urlStem", ctxt.GetScratch("ConferenceLink").(string))
ctxt.VarMap().Set("topicName", "")
pseud, err := conf.DefaultPseud(ctxt.Ctx(), ctxt.CurrentUser())
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")
}
urlStem := fmt.Sprintf("/comm/%s/conf/%s", comm.Alias, ctxt.GetScratch("currentAlias"))
urlStem := ctxt.GetScratch("ConferenceLink").(string)
if ctxt.FormFieldIsSet("cancel") {
return "redirect", urlStem
}
@@ -247,7 +246,7 @@ func NewTopic(ctxt ui.AmContext) (string, any) {
if err != nil {
return "error", err
}
checker.SetContext("PostLinkDecoderContext", database.AmCreatePostLinkContext(comm.Alias, ctxt.GetScratch("currentAlias").(string), conf.TopTopic+1))
checker.SetContext("PostLinkDecoderContext", database.AmCreatePostLinkContext(comm.Alias, comm.Id, ctxt.GetScratch("currentAlias").(string), conf.TopTopic+1))
checker.Append(postdata)
checker.Finish()
v, _ = checker.Value()
@@ -282,7 +281,7 @@ func NewTopic(ctxt ui.AmContext) (string, any) {
if err != nil {
return "error", err
}
checker.SetContext("PostLinkDecoderContext", database.AmCreatePostLinkContext(comm.Alias, ctxt.GetScratch("currentAlias").(string), 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.Finish()
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.
func templateOverrideLine(args jet.Arguments) reflect.Value {
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 := ""
if post.IsScribbled() {
scr_date := ""
@@ -402,7 +402,7 @@ func templateOverrideLine(args jet.Arguments) reflect.Value {
} else {
rc = fmt.Sprintf("<<<%v>>>", err)
}
} else if post.Hidden {
} else if post.Hidden && !advanced {
rc = fmt.Sprintf("(Hidden Message: %d Lines)", *post.LineCount)
}
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.
func templateOverrideLink(args jet.Arguments) reflect.Value {
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 := ""
if post.Hidden {
if post.Hidden && !advanced {
rc = fmt.Sprintf("%s?r=%d&ac=1", root, post.Num)
}
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))
ctxt.VarMap().Set("summaryLine", 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())
plc.Community = comm.Alias
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.LastPost = postRange[1]
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)
// 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 {
traverser.ClearTopic(topic.Number)
nextTopic := traverser.NextTopic(topic.Number)
@@ -686,7 +688,7 @@ func PostInTopic(ctxt ui.AmContext) (string, any) {
topic := ctxt.GetScratch("currentTopic").(*database.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") {
return "redirect", urlStem
}
@@ -734,7 +736,7 @@ func PostInTopic(ctxt ui.AmContext) (string, any) {
if err != nil {
return "error", err
}
checker.SetContext("PostLinkDecoderContext", database.AmCreatePostLinkContext(comm.Alias, ctxt.GetScratch("currentAlias").(string), topic.Number))
checker.SetContext("PostLinkDecoderContext", database.AmCreatePostLinkContext(comm.Alias, comm.Id, ctxt.GetScratch("currentAlias").(string), topic.Number))
checker.Append(postdata)
checker.Finish()
v, _ = checker.Value()
@@ -754,7 +756,7 @@ func PostInTopic(ctxt ui.AmContext) (string, any) {
} else if ctxt.FormFieldIsSet("postnext") && len(urlNextTopic) > 0 {
returnURL = urlNextTopic
} else if ctxt.FormFieldIsSet("posttopics") {
returnURL = fmt.Sprintf("/comm/%s/conf/%s", comm.Alias, ctxt.GetScratch("currentAlias"))
returnURL = ctxt.GetScratch("ConferenceLink").(string)
} else {
return "error", EBUTTON
}
@@ -772,10 +774,13 @@ func PostInTopic(ctxt ui.AmContext) (string, any) {
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())
plc.Community = comm.Alias
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_getOverrideLink", templateOverrideLink)
@@ -783,7 +788,6 @@ func PostInTopic(ctxt ui.AmContext) (string, any) {
ctxt.VarMap().SetFunc("post_getUserName", templateExtractUserName)
ctxt.VarMap().SetFunc("post_getAttachmentInfo", templateAttachmentInfo)
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("posts", posts)
ctxt.VarMap().Set("topicName", topic.Name)
@@ -805,7 +809,8 @@ func PostInTopic(ctxt ui.AmContext) (string, any) {
if err != nil {
return "error", err
}
checker.SetContext("PostLinkDecoderContext", database.AmCreatePostLinkContext(comm.Alias, ctxt.GetScratch("currentAlias").(string), topic.Number))
checker.SetContext("PostLinkDecoderContext", database.AmCreatePostLinkContext(comm.Alias, comm.Id,
ctxt.GetScratch("currentAlias").(string), topic.Number))
checker.Append(ctxt.FormField("pb"))
checker.Finish()
postText, _ := checker.Value()
+29 -33
View File
@@ -156,7 +156,7 @@ func ConfManage(ctxt ui.AmContext) (string, any) {
comm := ctxt.CurrentCommunity()
conf := ctxt.GetScratch("currentConference").(*database.Conference)
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("urlStem", urlStem)
@@ -193,14 +193,13 @@ func ConfManage(ctxt ui.AmContext) (string, any) {
* Data as a parameter for the command string.
*/
func SetPseud(ctxt ui.AmContext) (string, any) {
comm := ctxt.CurrentCommunity()
conf := ctxt.GetScratch("currentConference").(*database.Conference)
pseud := ctxt.FormField("pseud")
err := conf.SetDefaultPseud(ctxt.Ctx(), ctxt.CurrentUser(), pseud)
if err != nil {
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.
@@ -211,13 +210,12 @@ func SetPseud(ctxt ui.AmContext) (string, any) {
* Data as a parameter for the command string.
*/
func ConfFixseen(ctxt ui.AmContext) (string, any) {
comm := ctxt.CurrentCommunity()
conf := ctxt.GetScratch("currentConference").(*database.Conference)
err := conf.Fixseen(ctxt.Ctx(), ctxt.CurrentUser())
if err != nil {
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..
@@ -234,7 +232,7 @@ func AddToHotlist(ctxt ui.AmContext) (string, any) {
if err != nil {
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.
@@ -254,7 +252,7 @@ func HideTopic(ctxt ui.AmContext) (string, any) {
if err != nil {
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.
@@ -275,7 +273,7 @@ func FreezeTopic(ctxt ui.AmContext) (string, any) {
if err != nil {
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.
@@ -296,7 +294,7 @@ func ArchiveTopic(ctxt ui.AmContext) (string, any) {
if err != nil {
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.
@@ -317,7 +315,7 @@ func StickTopic(ctxt ui.AmContext) (string, any) {
if err != nil {
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.
@@ -348,14 +346,14 @@ func DeleteTopic(ctxt ui.AmContext) (string, any) {
if err != nil {
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.
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))
mbox.SetLink("no", fmt.Sprintf("/comm/%s/conf/%s/r/%d", ctxt.CurrentCommunity().Alias, ctxt.GetScratch("currentAlias"), topic.Number))
mbox.SetLink("yes", fmt.Sprintf("/comm/%s/conf/%s/op/%d/delete", 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("%s/op/%d/delete", ctxt.GetScratch("ConferenceLink"), topic.Number))
return mbox.Render(ctxt)
}
@@ -390,7 +388,7 @@ func HideMessage(ctxt ui.AmContext) (string, any) {
if err != nil {
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.
@@ -424,7 +422,7 @@ func ScribbleMessage(ctxt ui.AmContext) (string, any) {
if err != nil {
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.
@@ -466,11 +464,11 @@ func NukeMessage(ctxt ui.AmContext) (string, any) {
if err != nil {
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.
link, err := hdrs[0].Link(ctxt.Ctx(), "community")
link, err := hdrs[0].Link(ctxt.Ctx(), ctxt.CurrentCommunity().Id, "community")
if err != nil {
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>,
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("yes", fmt.Sprintf("/comm/%s/conf/%s/op/%d/nuke/%d", 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("%s/op/%d/nuke/%d", ctxt.GetScratch("ConferenceLink"), topic.Number, hdrs[0].Num))
return mbox.Render(ctxt)
}
@@ -524,11 +522,11 @@ func PruneMessageAttachment(ctxt ui.AmContext) (string, any) {
if err != nil {
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.
link, err := hdrs[0].Link(ctxt.Ctx(), "community")
link, err := hdrs[0].Link(ctxt.Ctx(), ctxt.CurrentCommunity().Id, "community")
if err != nil {
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>,
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("yes", fmt.Sprintf("/comm/%s/conf/%s/op/%d/prune/%d", 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("%s/op/%d/prune/%d", ctxt.GetScratch("ConferenceLink"), topic.Number, hdrs[0].Num))
return mbox.Render(ctxt)
}
@@ -591,7 +589,7 @@ func MoveMessageForm(ctxt ui.AmContext) (string, any) {
ctxt.VarMap().Set("post", hdrs[0])
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.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 {
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.
@@ -654,7 +652,7 @@ func MoveMessage(ctxt ui.AmContext) (string, any) {
return "error", EPOSTREF
}
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 {
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.
@@ -709,8 +707,8 @@ func MoveMessage(ctxt ui.AmContext) (string, any) {
func TopicManage(ctxt ui.AmContext) (string, any) {
comm := ctxt.CurrentCommunity()
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))
opsLink := fmt.Sprintf("/comm/%s/conf/%s/op/%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("%s/op/%d", ctxt.GetScratch("ConferenceLink"), topic.Number)
ctxt.VarMap().Set("opsLink", opsLink)
ctxt.VarMap().Set("topicName", topic.Name)
@@ -750,7 +748,6 @@ func TopicSetSubscribe(ctxt ui.AmContext) (string, any) {
if ctxt.CurrentUser().IsAnon {
return "error", ENOPERM
}
comm := ctxt.CurrentCommunity()
topic := ctxt.GetScratch("currentTopic").(*database.Topic)
flag, err := topic.IsSubscribed(ctxt.Ctx(), ctxt.CurrentUser())
if err != nil {
@@ -760,7 +757,7 @@ func TopicSetSubscribe(ctxt ui.AmContext) (string, any) {
if err != nil {
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.
@@ -771,7 +768,6 @@ func TopicSetSubscribe(ctxt ui.AmContext) (string, any) {
* Data as a parameter for the command string.
*/
func TopicRemoveBozo(ctxt ui.AmContext) (string, any) {
comm := ctxt.CurrentCommunity()
topic := ctxt.GetScratch("currentTopic").(*database.Topic)
bozoUid, err := strconv.Atoi(ctxt.URLParam("uid"))
if err != nil {
@@ -781,5 +777,5 @@ func TopicRemoveBozo(ctxt ui.AmContext) (string, any) {
if err != nil {
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)
}
+33 -45
View File
@@ -99,7 +99,7 @@ func EditConference(ctxt ui.AmContext) (string, any) {
}
button := dlg.WhichButton(ctxt)
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" {
dlg.SetCommunity(comm)
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 "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.
@@ -148,8 +148,8 @@ func ConferenceAliasForm(ctxt ui.AmContext) (string, any) {
ctxt.VarMap().Set("newAlias", "")
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("selfLink", fmt.Sprintf("/comm/%s/conf/%s/aliases", comm.Alias, ctxt.GetScratch("currentAlias")))
ctxt.VarMap().Set("backLink", fmt.Sprintf("%s/manage", ctxt.GetScratch("ConferenceLink")))
ctxt.VarMap().Set("selfLink", fmt.Sprintf("%s/aliases", ctxt.GetScratch("ConferenceLink")))
ctxt.SetFrameTitle(fmt.Sprintf("Manage Conference Aliases: %s", conf.Name))
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 {
return "error", err
}
@@ -184,8 +184,8 @@ func ConferenceAliasAdd(ctxt ui.AmContext) (string, any) {
}
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("selfLink", fmt.Sprintf("/comm/%s/conf/%s/aliases", comm.Alias, ctxt.GetScratch("currentAlias")))
ctxt.VarMap().Set("backLink", fmt.Sprintf("%s/manage", ctxt.GetScratch("ConferenceLink")))
ctxt.VarMap().Set("selfLink", fmt.Sprintf("%s/aliases", ctxt.GetScratch("ConferenceLink")))
ctxt.SetFrameTitle(fmt.Sprintf("Manage Conference Aliases: %s", conf.Name))
newAlias := ctxt.FormField("na")
@@ -206,7 +206,7 @@ func ConferenceAliasAdd(ctxt ui.AmContext) (string, any) {
ctxt.VarMap().Set("errorMessage", err.Error())
}
aliases, err := conf.Aliases(ctxt.Ctx())
aliases, err := conf.Aliases(ctxt.Ctx(), comm.Id)
if err != nil {
return "error", err
}
@@ -241,8 +241,8 @@ func ConferenceMembers(ctxt ui.AmContext) (string, any) {
// Set the first batch of page variables.
ctxt.VarMap().Set("commName", comm.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("selfLink", fmt.Sprintf("/comm/%s/conf/%s/members", comm.Alias, ctxt.GetScratch("currentAlias")))
ctxt.VarMap().Set("backLink", fmt.Sprintf("%s/manage", ctxt.GetScratch("ConferenceLink")))
ctxt.VarMap().Set("selfLink", fmt.Sprintf("%s/members", ctxt.GetScratch("ConferenceLink")))
ctxt.VarMap().Set("roleList", database.AmRoleList("Conference.UserLevels"))
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.
*/
func ConfCustomForm(ctxt ui.AmContext) (string, any) {
comm := ctxt.CurrentCommunity()
conf := ctxt.GetScratch("currentConference").(*database.Conference)
myLevel := ctxt.GetScratch("levelInConference").(uint16)
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("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("bottomText", bottomBlock)
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.
*/
func ConfCustom(ctxt ui.AmContext) (string, any) {
comm := ctxt.CurrentCommunity()
conf := ctxt.GetScratch("currentConference").(*database.Conference)
myLevel := ctxt.GetScratch("levelInConference").(uint16)
if !conf.TestPermission("Conference.Change", myLevel) {
@@ -429,7 +427,7 @@ func ConfCustom(ctxt ui.AmContext) (string, any) {
if err != nil {
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.
@@ -440,7 +438,6 @@ func ConfCustom(ctxt ui.AmContext) (string, any) {
* Data as a parameter for the command string.
*/
func ConfReports(ctxt ui.AmContext) (string, any) {
comm := ctxt.CurrentCommunity()
conf := ctxt.GetScratch("currentConference").(*database.Conference)
myLevel := ctxt.GetScratch("levelInConference").(uint16)
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("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") {
// generate a report
@@ -504,7 +501,7 @@ func ConfReports(ctxt ui.AmContext) (string, any) {
return "error", err
}
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))
return "framed", "conf_reports.jet"
}
@@ -518,7 +515,6 @@ func ConfReports(ctxt ui.AmContext) (string, any) {
* Data as a parameter for the command string.
*/
func ConferenceEmailForm(ctxt ui.AmContext) (string, any) {
comm := ctxt.CurrentCommunity()
conf := ctxt.GetScratch("currentConference").(*database.Conference)
myLevel := ctxt.GetScratch("levelInConference").(uint16)
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("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("day", 7).Set("subj", "").Set("pb", "")
ctxt.SetFrameTitle(fmt.Sprintf("Conference E-Mail: %s", conf.Name))
@@ -555,7 +551,7 @@ func ConferenceEmail(ctxt ui.AmContext) (string, any) {
// Handle button presses.
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") {
return "error", EBUTTON
}
@@ -646,7 +642,7 @@ func ConferenceEmail(ctxt ui.AmContext) (string, any) {
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.
@@ -657,7 +653,6 @@ func ConferenceEmail(ctxt ui.AmContext) (string, any) {
* Data as a parameter for the command string.
*/
func ConferenceExportForm(ctxt ui.AmContext) (string, any) {
comm := ctxt.CurrentCommunity()
conf := ctxt.GetScratch("currentConference").(*database.Conference)
myLevel := ctxt.GetScratch("levelInConference").(uint16)
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("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))
return "framed", "conf_export.jet"
}
@@ -684,7 +679,6 @@ func ConferenceExportForm(ctxt ui.AmContext) (string, any) {
* Data as a parameter for the command string.
*/
func ConferenceExport(ctxt ui.AmContext) (string, any) {
comm := ctxt.CurrentCommunity()
conf := ctxt.GetScratch("currentConference").(*database.Conference)
myLevel := ctxt.GetScratch("levelInConference").(uint16)
if !conf.TestPermission("Conference.Change", myLevel) {
@@ -692,7 +686,7 @@ func ConferenceExport(ctxt ui.AmContext) (string, any) {
}
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") {
return "error", EBUTTON
}
@@ -765,15 +759,16 @@ func ConferenceImport(ctxt ui.AmContext) (string, any) {
return "error", ENOPERM
}
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.VarMap().Set("selfLink", fmt.Sprintf("%s/import", ctxt.GetScratch("ConferenceLink")))
ctxt.SetFrameTitle("Import Messages: " + conf.Name)
if ctxt.Verb() == "GET" {
return "framed", "conf_import.jet"
}
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") {
return "error", EBUTTON
}
@@ -786,27 +781,18 @@ func ConferenceImport(ctxt ui.AmContext) (string, any) {
mode = exports.VCIFTopicMatchNum
default:
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"
}
importData, err := ctxt.FormFile("idata")
if err != nil {
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"
}
start := time.Now()
f, err := importData.Open()
if err != nil {
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"
}
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))
if err != nil {
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"
}
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("scroll", scroll)
ctxt.SetFrameTitle("Import Results")
@@ -858,8 +841,8 @@ func DeleteConference(ctxt ui.AmContext) (string, any) {
// 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>
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("yes", fmt.Sprintf("/comm/%s/conf/%s/delete", ctxt.CurrentCommunity().Alias, ctxt.GetScratch("currentAlias")))
mbox.SetLink("no", fmt.Sprintf("%s/manage", ctxt.GetScratch("ConferenceLink")))
mbox.SetLink("yes", fmt.Sprintf("%s/delete", ctxt.GetScratch("ConferenceLink")))
return mbox.Render(ctxt)
}
@@ -901,9 +884,12 @@ func CreateConference(ctxt ui.AmContext) (string, any) {
if err != nil {
return "error", err
}
var urlbuf strings.Builder
urlbuf.WriteString(ctxt.GetScratch("CommunityLink").(string))
urlbuf.WriteString("/conf")
button := dlg.WhichButton(ctxt)
if button == "cancel" {
return "redirect", fmt.Sprintf("/comm/%s/conf", comm.Alias)
return "redirect", urlbuf.String()
} else if button != "create" {
dlg.SetCommunity(comm)
return dlg.RenderError(ctxt, "invalid button pressed")
@@ -917,7 +903,9 @@ func CreateConference(ctxt ui.AmContext) (string, any) {
return dlg.RenderError(ctxt, err.Error())
}
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.
+2 -1
View File
@@ -30,7 +30,7 @@ import (
)
// AMSTERDAM_VERSION contains the version number of Amsterdam.
const AMSTERDAM_VERSION = "0.1.1"
const AMSTERDAM_VERSION = "0.2.0"
// AMSTERDAM_COPYRIGHT contains the copyright dates for Amsterdam.
const AMSTERDAM_COPYRIGHT = "2025-2026"
@@ -92,6 +92,7 @@ type AmConfig struct {
Type string `yaml:"type"`
} `yaml:"siteIcon"`
SiteShortcutIcon string `yaml:"siteShortcutIcon"`
SiteAppleIcon string `yaml:"siteAppleIcon"`
SiteLogo string `yaml:"siteLogo"`
TopRefresh int `yaml:"topRefresh"`
LoginCookieName string `yaml:"loginCookieName"`
+1
View File
@@ -17,6 +17,7 @@ site:
path: "/img/builtin/AmsterdamIcon32.png"
type: "image/png"
siteShortcutIcon: "/img/builtin/AmsterdamIcon32.ico"
siteAppleIcon: "/img/builtin/AmsterdamAppleIcon.png"
siteLogo: "/img/builtin/powered-by-amsterdam.png"
topRefresh: 300
loginCookieName: AmsterdamAuth
+1
View File
@@ -93,6 +93,7 @@ const (
AuditAdminChangeUserAccount = 111
AuditAdminSetAccountSecurity = 112
AuditAdminLockUnlockAccount = 113
AuditAdminSetUserName = 114
AuditCommunityCreate = 201
AuditCommunitySetMembership = 202
AuditCommunityContactInfo = 203
+2
View File
@@ -44,6 +44,8 @@ auditReference:
text: "Admin Set Account Security"
- code: 113
text: "Admin Lock/Unlock Account"
- code: 114
text: "Admin Set User Name"
- code: 201
text: "Create New Community"
- code: 202
+54 -59
View File
@@ -17,6 +17,7 @@ import (
"errors"
"fmt"
"slices"
"strconv"
"strings"
"sync"
"time"
@@ -152,8 +153,8 @@ func (cs *ConferenceSettings) Save(ctx context.Context) error {
}
// Aliases returns the list of aliases for this conference.
func (c *Conference) Aliases(ctx context.Context) ([]string, error) {
rs, err := amdb.QueryContext(ctx, "SELECT alias FROM confalias WHERE confid = ? ORDER BY alias", c.ConfId)
func (c *Conference) Aliases(ctx context.Context, commid int32) ([]string, error) {
rs, err := amdb.QueryContext(ctx, "SELECT alias FROM confalias WHERE commid = ? AND confid = ? ORDER BY alias", commid, c.ConfId)
if err != nil {
return nil, err
}
@@ -169,25 +170,22 @@ func (c *Conference) Aliases(ctx context.Context) ([]string, error) {
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.
func (c *Conference) AddAlias(ctx context.Context, alias string, u *User, comm *Community, ipaddr string) error {
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 {
return fmt.Errorf("the alias '%s' is already in use by another conference", alias)
}
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
}
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)))
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.
func (c *Conference) RemoveAlias(ctx context.Context, alias string, u *User, comm *Community, ipaddr string) error {
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
}
if aliasCount == 1 {
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 {
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
}
}
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 {
return err
}
@@ -221,6 +221,7 @@ func (c *Conference) RemoveAlias(ctx context.Context, alias string, u *User, com
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)))
return nil
}
@@ -431,8 +432,8 @@ func (c *Conference) Settings(ctx context.Context, u *User) (*ConferenceSettings
}
// Link returns a link string to this conference.
func (c *Conference) Link(ctx context.Context, scope string) (string, error) {
aliases, err := c.Aliases(ctx)
func (c *Conference) Link(ctx context.Context, commid int32, scope string) (string, error) {
aliases, err := c.Aliases(ctx, commid)
if err != nil {
return "", err
}
@@ -440,9 +441,9 @@ func (c *Conference) Link(ctx context.Context, scope string) (string, error) {
return fmt.Sprintf("%s.", aliases[0]), nil
}
if scope == "global" {
comms, err := c.ContainedBy(ctx)
comm, err := AmGetCommunity(ctx, commid)
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
}
@@ -872,22 +873,22 @@ func (c *Conference) Delete(ctx context.Context, comm *Community, u *User, ipadd
// any references to conference other than this community?
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
}
// 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
}
var err error
if refCount == 0 {
// We have to delete all the conference core data now.
_, 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 {
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
conferenceCache.Remove(c.ConfId)
// simpler to just nuke the entire alias map
conferenceAliasMap.Clear()
// add an audit record
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 {
return err
}
if _, err = tx.ExecContext(ctx, "DELETE FROM confalias WHERE commid = ? AND confid = ?", commid, confid); err != nil {
return err
}
if refCount > 0 {
confids[i] = -1
continue // done with this conference
}
// We have to delete all the conference core data now.
_, err = tx.ExecContext(ctx, "DELETE FROM confs WHERE confid = ?", confid)
if err == nil {
_, err = tx.ExecContext(ctx, "DELETE FROM confalias WHERE confid = ?", confid)
}
if err != nil {
if _, err = tx.ExecContext(ctx, "DELETE FROM confs WHERE confid = ?", confid); err != nil {
return err
}
// kick the conference out of the cache
@@ -973,7 +976,8 @@ func (*conferenceServiceVTable) OnDeleteCommunity(ctx context.Context, tx *sqlx.
getConferenceMutex.Unlock()
}
// Just dump the whole conference property cache.
// Just dump the whole conference alias map and property cache.
conferenceAliasMap.Clear()
getConferencePropMutex.Lock()
conferencePropCache.Purge()
getConferencePropMutex.Unlock()
@@ -1030,27 +1034,38 @@ func AmGetConference(ctx context.Context, id int32) (*Conference, error) {
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.
* Parameters:
* ctx - Standard Go context value.
*. commid - Community ID to look under.
* alias - The alias to look up.
* Returns:
* Pointer to the conference, or nil.
* 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
xconf, ok := conferenceAliasMap.Load(alias)
key := confAliasMapKey(commid, alias)
xconf, ok := conferenceAliasMap.Load(key)
if ok {
confid = xconf.(int32)
} 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 {
return nil, fmt.Errorf("alias not found: %s", alias)
} else if err != nil {
return nil, err
}
conferenceAliasMap.Store(alias, confid)
conferenceAliasMap.Store(key, confid)
}
return AmGetConference(ctx, confid)
}
@@ -1074,28 +1089,6 @@ func AmGetConferenceContainingPost(ctx context.Context, postId int64) (*Conferen
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.
* Parameters:
* ctx - Standard Go context value.
@@ -1125,7 +1118,7 @@ func AmListConferences(ctx context.Context, cid int32, showHidden bool) ([]*Conf
}
}
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 {
return nil, err
}
@@ -1271,7 +1264,7 @@ func AmCreateConference(ctx context.Context, comm *Community, name, alias, descr
// Ensure the alias is not in use.
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 {
return nil, fmt.Errorf("the alias '%s' is already in use by a different conference", alias)
} 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.
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
}
@@ -1329,6 +1323,7 @@ func AmCreateConference(ctx context.Context, comm *Community, name, alias, descr
// Add the new conference to the cache.
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.
fcomm, err := comm.Flags(ctx)
+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;
+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.
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" {
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 {
return "", err
}
parent, err := topic.Link(ctx, scope)
parent, err := topic.Link(ctx, commid, scope)
if err != nil {
return "", err
}
@@ -628,47 +628,55 @@ func AmNewPost(ctx context.Context, conf *Conference, topic *Topic, user *User,
* Array of post headers, or nil.
* Standard Go error status.
*/
func AmGetPublishedPosts(ctx context.Context) ([]*PostHeader, error) {
func AmGetPublishedPosts(ctx context.Context) ([]*PostHeader, []*Community, error) {
// Read the globals.
gv, err := AmGlobals(ctx)
if err != nil {
return nil, err
return nil, nil, err
}
// 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 {
return nil, err
return nil, nil, err
}
// Extract post IDs to an array.
cids := make([]int32, gv.FrontPagePosts)
pids := make([]int64, gv.FrontPagePosts)
i := 0
for i < int(gv.FrontPagePosts) && rs.Next() {
if err = rs.Scan(&(pids[i])); err != nil {
return nil, err
if err = rs.Scan(&(cids[i]), &(pids[i])); err != nil {
return nil, nil, err
}
i++
}
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) {
cids = cids[:i]
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.
query, args, err := sqlx.In("SELECT * FROM posts WHERE postid IN (?)", pids)
if err != nil {
return nil, err
return nil, nil, err
}
query = amdb.Rebind(query)
// Use the SQL to read in all the post headers using a single database query.
var data []PostHeader
if err = amdb.SelectContext(ctx, &data, query, args...); err != nil {
return nil, err
return nil, nil, err
}
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.
@@ -683,10 +691,10 @@ func AmGetPublishedPosts(ctx context.Context) ([]*PostHeader, error) {
}
}
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 {
@@ -898,13 +906,13 @@ func AmSearchPosts(ctx context.Context, searchTerms string, u *User, offset, max
if err != nil {
return nil, count, err
}
alias, err := conf.Aliases(ctx)
alias, err := conf.Aliases(ctx, commid)
if err != nil {
return nil, count, err
}
// Build the post link.
plink := AmCreatePostLinkContext(commAlias, alias[0], topicNum)
plink := AmCreatePostLinkContext(commAlias, commid, alias[0], topicNum)
plink.FirstPost = postnum
plink.LastPost = postnum
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.
type PostLinkData struct {
Community string
CommId int32
Conference string
Topic int16
FirstPost int32
@@ -36,6 +37,7 @@ func (d *PostLinkData) NeedsDBVerification() bool {
// VerifyNames verifies the post link data against the database.
func (d *PostLinkData) VerifyNames(ctx context.Context) error {
commid := d.CommId
if d.Community != "" {
comm, err := AmGetCommunityByAlias(ctx, d.Community)
if err != nil {
@@ -44,9 +46,10 @@ func (d *PostLinkData) VerifyNames(ctx context.Context) error {
if comm == nil {
return errors.New("community alias not found")
}
commid = comm.Id
}
if d.Conference != "" {
conf, err := AmGetConferenceByAlias(ctx, d.Conference)
conf, err := AmGetConferenceByAlias(ctx, commid, d.Conference)
if err != nil {
return err
}
@@ -392,9 +395,10 @@ func AmDecodePostLink(data string) (*PostLinkData, error) {
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{
Community: community,
CommId: commid,
Conference: conference,
Topic: topic,
FirstPost: -1,
+2 -2
View File
@@ -43,7 +43,7 @@ type Topic struct {
}
// 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" {
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)
if err == nil {
var plink string
plink, err = conf.Link(ctx, scope)
plink, err = conf.Link(ctx, commid, scope)
if err == nil {
if strings.HasSuffix(plink, ".") {
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
}
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.
* Parameters:
* ctx - Standard Go context value.
+17
View File
@@ -1,5 +1,22 @@
# 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).
+1 -1
View File
@@ -88,7 +88,7 @@ func AmDeliverSubscription(ctx context.Context, comm *database.Community, conf *
vars.Set("communityName", comm.Name)
vars.Set("conferenceName", conf.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("pseud", realPseud)
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.
*/
func FindPostsPageConference(ctxt ui.AmContext) (string, any) {
comm := ctxt.CurrentCommunity()
conf := ctxt.GetScratch("currentConference").(*database.Conference)
ctxt.VarMap().Set("scope", "conference")
ctxt.VarMap().Set("entityName", conf.Name)
ctxt.VarMap().Set("backlink", fmt.Sprintf("/comm/%s/conf/%s", comm.Alias, ctxt.GetScratch("currentAlias")))
ctxt.VarMap().Set("postlink", fmt.Sprintf("/comm/%s/conf/%s/find", comm.Alias, ctxt.GetScratch("currentAlias")))
ctxt.VarMap().Set("backlink", ctxt.GetScratch("ConferenceLink").(string))
ctxt.VarMap().Set("postlink", fmt.Sprintf("%s/find", ctxt.GetScratch("ConferenceLink")))
return commonFindGetBackend(ctxt)
}
@@ -362,12 +361,11 @@ func FindPostsPageConference(ctxt ui.AmContext) (string, any) {
* Data as a parameter for the command string.
*/
func FindPostsPageTopic(ctxt ui.AmContext) (string, any) {
comm := ctxt.CurrentCommunity()
topic := ctxt.GetScratch("currentTopic").(*database.Topic)
ctxt.VarMap().Set("scope", "topic")
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("postlink", fmt.Sprintf("/comm/%s/conf/%s/op/%d/find", 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("%s/op/%d/find", ctxt.GetScratch("ConferenceLink"), topic.Number))
return commonFindGetBackend(ctxt)
}
@@ -438,8 +436,8 @@ func FindPostsConference(ctxt ui.AmContext) (string, any) {
conf := ctxt.GetScratch("currentConference").(*database.Conference)
ctxt.VarMap().Set("scope", "conference")
ctxt.VarMap().Set("entityName", conf.Name)
ctxt.VarMap().Set("backlink", fmt.Sprintf("/comm/%s/conf/%s", comm.Alias, ctxt.GetScratch("currentAlias")))
ctxt.VarMap().Set("postlink", fmt.Sprintf("/comm/%s/conf/%s/find", comm.Alias, ctxt.GetScratch("currentAlias")))
ctxt.VarMap().Set("backlink", ctxt.GetScratch("ConferenceLink").(string))
ctxt.VarMap().Set("postlink", fmt.Sprintf("%s/find", ctxt.GetScratch("ConferenceLink")))
return commonFindPostBackend(ctxt, comm, conf, nil)
}
@@ -456,7 +454,7 @@ func FindPostsTopic(ctxt ui.AmContext) (string, any) {
topic := ctxt.GetScratch("currentTopic").(*database.Topic)
ctxt.VarMap().Set("scope", "topic")
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("postlink", fmt.Sprintf("/comm/%s/conf/%s/op/%d/find", 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("%s/op/%d/find", ctxt.GetScratch("ConferenceLink"), topic.Number))
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.2.0 h1:crnVqOiS4jqYleHd9vaKZ+HKtHfllngJIiOpNpoJsjo=
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-20251202014920-1725d2651bd4 h1:DQ1+lDdBve+u+aovjh4wV6sYnvZKH0Hx8GaQOi4vYl8=
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/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/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/go.mod h1:LoFvNMqS1CPrMVltza4LvnGKhaSpc3oyLEBUZVhhS2o=
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/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/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/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
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/klauspost/lctime v0.1.0 h1:nINsuFc860M9cyYhT6vfg6U1USh7kiVBj/s/2b04U70=
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/go.mod h1:xmw1clThob0BSVRX1CRQkGQ/vjwcpOMjQZSZa9fKA/c=
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/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
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/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.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/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
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/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
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/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/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.37.0 h1:ZiRjArKI8GwxZOoEtUfhrBtaCN+4b/7709dlT6SSnQA=
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/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.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/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
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/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/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/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/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+2
View File
@@ -127,6 +127,7 @@ func buildPostLink(decoded, context *database.PostLinkData) string {
b.WriteString(context.Conference)
} else {
b.WriteString(decoded.Conference)
started = true
}
b.WriteString(".")
if decoded.Topic == -1 {
@@ -168,6 +169,7 @@ func (rw *postLinkRewriter) Rewrite(ctx context.Context, data string, svc rewrit
if err != nil {
return nil
}
mydata.CommId = ctxt.CommId
err = mydata.VerifyNames(ctx)
if err != nil {
return nil
+2 -2
View File
@@ -59,7 +59,7 @@ func InviteToConference(ctxt ui.AmContext) (string, any) {
ctxt.SetFrameTitle("Send Invitation")
ctxt.VarMap().Set("title", "Send Conference Invitation")
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("confid", fmt.Sprintf("%d", conf.ConfId))
return "framed", "invite.jet"
@@ -83,7 +83,7 @@ func InviteToTopic(ctxt ui.AmContext) (string, any) {
ctxt.SetFrameTitle("Send Invitation")
ctxt.VarMap().Set("title", "Send Topic Invitation")
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("confid", fmt.Sprintf("%d", conf.ConfId))
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
prefs, err = user.Prefs(ctxt.Ctx())
if err == nil {
dlg.Field("user").Value = user.Username
dlg.Field("remind").Value = user.PassReminder
dlg.Field("base_lvl").SetLevel(user.BaseLevel)
dlg.Field("verify_email").SetChecked(user.VerifyEMail)
@@ -327,6 +328,12 @@ func UserManagementSave(ctxt ui.AmContext) (string, any) {
if err == nil {
var prefs *database.UserPrefs
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()) {
p1 := dlg.Field("pass1").Value
if p1 == dlg.Field("pass2").Value {
@@ -378,6 +385,9 @@ func UserManagementSave(ctxt ui.AmContext) (string, any) {
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 {
err = user.SetProfileData(ctxt.Ctx(), dlg.Field("remind").Value, dlg.Field("dob").AsDate(), dlg.Field("descr").ValPtr(),
ctxt.CurrentUser(), ctxt.RemoteIP())
+7 -5
View File
@@ -93,7 +93,7 @@ func renderSBConferences(ctx context.Context, u *database.User, sb *DisplaySideb
return err
}
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
}
alias[i] = a[0]
@@ -176,9 +176,10 @@ func templateGetTopic(args jet.Arguments) reflect.Value {
// templateTopicLink returns the link string for the given topic.
func templateTopicLink(args jet.Arguments) reflect.Value {
topic := args.Get(0).Convert(reflect.TypeFor[*database.Topic]()).Interface().(*database.Topic)
ctxt := args.Get(1).Convert(reflect.TypeFor[ui.AmContext]()).Interface().(ui.AmContext)
link, _ := topic.Link(ctxt.Ctx(), "global")
comm := args.Get(0).Convert(reflect.TypeFor[*database.Community]()).Interface().(*database.Community)
topic := args.Get(1).Convert(reflect.TypeFor[*database.Topic]()).Interface().(*database.Topic)
ctxt := args.Get(2).Convert(reflect.TypeFor[ui.AmContext]()).Interface().(ui.AmContext)
link, _ := topic.Link(ctxt.Ctx(), comm.Id, "global")
return reflect.ValueOf(link)
}
@@ -194,12 +195,13 @@ func TopPage(ctxt ui.AmContext) (string, any) {
ctxt.SetFrameTitle("My Front Page")
// Retrieve the published posts.
hdrs, err := database.AmGetPublishedPosts(ctxt.Ctx())
hdrs, comms, err := database.AmGetPublishedPosts(ctxt.Ctx())
if err != nil {
return "error", err
}
ctxt.VarMap().Set("posts", hdrs)
ctxt.VarMap().Set("comms", comms)
ctxt.VarMap().SetFunc("post_getText", templatePostText)
ctxt.VarMap().SetFunc("post_getUserName", templateExtractUserName)
ctxt.VarMap().SetFunc("post_topic", templateGetTopic)
+2 -1
View File
@@ -342,7 +342,6 @@ func (st *amSessionStore) SessionInfo() (int, []string, int) {
func (st *amSessionStore) sweep(tick <-chan time.Time, done chan bool) {
for range tick {
if st.sweepRunning.Load() {
log.Infof("session sweep running")
// phase 1 - identify expired sessions
st.mutex.RLock()
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()
if len(zap) > 0 {
log.Infof("identified %d sessions to zap", len(zap))
}
// phase 2 - get rid of the expired sessions
for _, k := range zap {
+9
View File
@@ -15,6 +15,15 @@ title: "Modify User Account"
subtitle: "User: [USERNAME]"
action: "/sysadmin/users/[USERNAME]"
fields:
- type: "header"
name: "header0"
caption: "User Information"
- type: "ams_id"
name: "user"
caption: "User Name"
required: true
size: 32
maxlength: 64
- type: "header"
name: "header1"
caption: "Security Information"
+11 -1
View File
@@ -17,6 +17,7 @@ import (
"net/http"
"net/url"
"strconv"
"strings"
"git.erbosoft.com/amy/amsterdam/config"
"git.erbosoft.com/amy/amsterdam/database"
@@ -82,6 +83,10 @@ func SetCommunity(next echo.HandlerFunc) echo.HandlerFunc {
if err != nil {
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")
return next(c)
}
@@ -113,7 +118,7 @@ func ValidateConference(next echo.HandlerFunc) echo.HandlerFunc {
func SetConference(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
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 {
return AmSendPageData(c, ctxt, "error", err)
}
@@ -128,6 +133,11 @@ func SetConference(next echo.HandlerFunc) echo.HandlerFunc {
ctxt.SetScratch("currentConference", conf)
ctxt.SetScratch("currentAlias", ctxt.URLParam("confid"))
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)
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

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