landed E-mail invites at three levels

This commit is contained in:
2026-01-30 14:44:30 -07:00
parent a0064b37a5
commit 2c9ceefd6b
7 changed files with 185 additions and 7 deletions
+14
View File
@@ -112,6 +112,20 @@ func (c *Conference) Hosts(ctx context.Context) ([]*User, error) {
return rc, nil
}
// InCommunity returns true if the specified conference is in the community.
func (c *Conference) InCommunity(ctx context.Context, comm *Community) (bool, error) {
row := amdb.QueryRowContext(ctx, "SELECT commid FROM commtoconf WHERE commid = ? AND confid = ?", comm.Id, c.ConfId)
var tmp int32
err := row.Scan(&tmp)
switch err {
case nil:
return true, nil
case sql.ErrNoRows:
return false, nil
}
return false, err
}
// ContainedBy returns the communities that contain this conference.
func (c *Conference) ContainedBy(ctx context.Context) ([]*Community, error) {
rs, err := amdb.QueryContext(ctx, "SELECT commid FROM commtoconf WHERE confid = ?", c.ConfId)
+4 -4
View File
@@ -25,16 +25,16 @@ _(italicized items can be deferred)_
- E-Mail to All Members
- Display Audit Records
- Delete Community
- Community Profile: Invite
- ~~Community Profile: Invite~~
- _Help link atop page headers_
- _Policy page_
- Member List: Export
- HTML reference for post boxes
- Posts view:
- Find
- Manage:
- ~~Manage:~~
- ~~Subscribe to Topic~~
- Send invite
- ~~Send invite~~
- ~~Filtered Users (list/remove)~~
- ~~Stick/Unstick~~
- ~~Freeze/Unfreeze~~
@@ -55,7 +55,7 @@ _(italicized items can be deferred)_
- Manage:
- Set pseud
- Fixseen
- Send invite
- ~~Send invite~~
- Change information
- Manage aliases
- Manage members
+35
View File
@@ -0,0 +1,35 @@
{*
* 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/.
*}
{{ .SetSubject("Invitation to " + comm.Name + " Community")}}
Hi! I would like to invite you to join the "{{ comm.Name }}" community on
{{ GlobalConfig.Site.Title }}. To do so, you must register as a user, which
is absolutely free! Just point your Web browser at
<{{ GlobalConfig.Site.BaseURL }}/comm/{{ comm.Alias }}> and click the "Create Account"
link at the top of the page, or click the "Log In" link if you already have
a Amsterdam account. Once you have completed the process, click the "Join Now"
button. You will be prompted for the "password" for this community, which is
"{{ comm.JoinKey }}". You will then be able to take part in the conferences that are
going on in the community.
{{ if mode == "conference" }}
After you've joined the "{{ comm.Name }}" community, check out the
"{{ conf.Name }}" conference. To find it, after joining the community,
click "Conferences" on the left menu bar, then click on the
"{{ conf.Name }}" conference name in the conference list.
{{ else if mode == "topic" }}
After you've joined the "{{ comm.Name }}" community, check out the
"{{ topic.Name | raw }}" topic in the "{{ conf.Name }}" conference. To find it,
after joining the community, click "Conferences" on the left menu bar, then
click on the "{{ conf.Name }}" conference name in the conference list,
then click on the "{{ topic.Name | raw }}" topic name in the topic list.
{{ end }}
{{ personal }}
Hope to see you in "{{ comm.Name }}" soon!
-- {{ fullname }} (Amsterdam user ID: {{ username }})
+34
View File
@@ -0,0 +1,34 @@
{*
* 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/.
*}
{{ .SetSubject("Invitation to " + comm.Name + " Community")}}
Hi! I would like to invite you to join the "{{ comm.Name }}" community on
{{ GlobalConfig.Site.Title }}. To do so, you must register as a user, which
is absolutely free! Just point your Web browser at
<{{ GlobalConfig.Site.BaseURL }}/comm/{{ comm.Alias }}> and click the "Create Account"
link at the top of the page, or click the "Log In" link if you already have
a Amsterdam account. Once you have completed the process, click the "Join Now"
button. You will then be able to take part in the conferences that are
going on in the community.
{{ if mode == "conference" }}
After you've joined the "{{ comm.Name }}" community, check out the
"{{ conf.Name }}" conference. To find it, after joining the community,
click "Conferences" on the left menu bar, then click on the
"{{ conf.Name }}" conference name in the conference list.
{{ else if mode == "topic" }}
After you've joined the "{{ comm.Name }}" community, check out the
"{{ topic.Name | raw }}" topic in the "{{ conf.Name }}" conference. To find it,
after joining the community, click "Conferences" on the left menu bar, then
click on the "{{ conf.Name }}" conference name in the conference list,
then click on the "{{ topic.Name | raw }}" topic name in the topic list.
{{ end }}
{{ personal }}
Hope to see you in "{{ comm.Name }}" soon!
-- {{ fullname }} (Amsterdam user ID: {{ username }})
+96 -2
View File
@@ -10,10 +10,13 @@
package main
import (
"errors"
"fmt"
"net/http"
"net/mail"
"git.erbosoft.com/amy/amsterdam/database"
"git.erbosoft.com/amy/amsterdam/email"
"git.erbosoft.com/amy/amsterdam/ui"
)
@@ -59,7 +62,7 @@ func InviteToConference(ctxt ui.AmContext) (string, any, error) {
ctxt.VarMap().Set("amsterdam_pageTitle", "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", comm.Alias, ctxt.GetScratch("currentAlias")))
ctxt.VarMap().Set("backlink", fmt.Sprintf("/comm/%s/conf/%s/manage", comm.Alias, ctxt.GetScratch("currentAlias")))
ctxt.VarMap().Set("cid", fmt.Sprintf("%d", comm.Id))
ctxt.VarMap().Set("confid", fmt.Sprintf("%d", conf.ConfId))
return "framed_template", "invite.jet", nil
@@ -85,9 +88,100 @@ func InviteToTopic(ctxt ui.AmContext) (string, any, error) {
ctxt.VarMap().Set("amsterdam_pageTitle", "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/r/%d", comm.Alias, ctxt.GetScratch("currentAlias"), topic.Number))
ctxt.VarMap().Set("backlink", fmt.Sprintf("/comm/%s/conf/%s/op/%d/manage", comm.Alias, ctxt.GetScratch("currentAlias"), 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))
return "framed_template", "invite.jet", nil
}
/* InviteSend is the back end that handles sending invitations.
* Parameters:
* ctxt - The AmContext for the request.
* Returns:
* Command string dictating what to be rendered.
* Data as a parameter for the command string.
* Standard Go error status.
*/
func InviteSend(ctxt ui.AmContext) (string, any, error) {
backlink := ctxt.FormField("backlink")
if ctxt.FormFieldIsSet("cancel") {
return "redirect", backlink, nil
} else if !ctxt.FormFieldIsSet("send") {
return ui.ErrorPage(ctxt, errors.New("invalid command"))
}
var comm *database.Community
if ctxt.FormFieldIsSet("cid") {
id, err := ctxt.FormFieldInt("cid")
if err == nil {
comm, err = database.AmGetCommunity(ctxt.Ctx(), int32(id))
}
if err != nil {
return ui.ErrorPage(ctxt, err)
}
} else {
return ui.ErrorPage(ctxt, errors.New("no parameters specified"))
}
mode := "community"
var conf *database.Conference = nil
var topic *database.Topic = nil
if ctxt.FormFieldIsSet("confid") {
id, err := ctxt.FormFieldInt("confid")
if err == nil {
if conf, err = database.AmGetConference(ctxt.Ctx(), int32(id)); err == nil {
var f bool
if f, err = conf.InCommunity(ctxt.Ctx(), comm); err == nil {
if !f {
err = errors.New("invalid conference; not in community")
}
}
}
}
if err != nil {
return ui.ErrorPage(ctxt, err)
}
if ctxt.FormFieldIsSet("topicid") {
id, err := ctxt.FormFieldInt("topicid")
if err == nil {
topic, err = database.AmGetTopic(ctxt.Ctx(), int32(id))
if err == nil && topic.ConfId != conf.ConfId {
err = errors.New("invalid topic; not in conference")
}
}
if err != nil {
return ui.ErrorPage(ctxt, err)
}
mode = "topic"
} else {
mode = "conference"
}
}
addr := ctxt.FormField("addr")
_, err := mail.ParseAddress(addr)
if err != nil {
return ui.ErrorPage(ctxt, err)
}
ci, err := database.AmGetContactInfoForUser(ctxt.Ctx(), ctxt.CurrentUserId())
if err != nil {
return ui.ErrorPage(ctxt, err)
}
mailMessage := email.AmNewEmailMessage(ctxt.CurrentUserId(), ctxt.RemoteIP())
if comm.Public() {
mailMessage.SetTemplate("invite_public.jet")
} else {
mailMessage.SetTemplate("invite_private.jet")
}
mailMessage.AddTo(addr, "")
mailMessage.AddVariable("comm", comm)
mailMessage.AddVariable("conf", conf)
mailMessage.AddVariable("topic", topic)
mailMessage.AddVariable("mode", mode)
mailMessage.AddVariable("personal", ctxt.FormField("msg"))
mailMessage.AddVariable("fullname", ci.FullName(true))
mailMessage.AddVariable("username", ctxt.CurrentUser().Username)
mailMessage.Send()
return "redirect", backlink, nil
}
+1
View File
@@ -77,6 +77,7 @@ func setupEcho() *echo.Echo {
e.POST("/create_comm", ui.AmWrap(CreateCommunity))
e.POST("/attachment_upload", ui.AmWrap(AttachmentUpload))
e.GET("/attachment/:post", ui.AmWrap(AttachmentSend))
e.POST("/__invite_send", ui.AmWrap(InviteSend))
// community group
commGroup := e.Group("/comm/:cid", ui.SetCommunity)
+1 -1
View File
@@ -16,7 +16,7 @@
<!-- Invitation Form -->
<div class="max-w-3xl">
<form method="POST" action="TODO">
<form method="POST" action="/__invite_send">
<input type="hidden" name="backlink" value="{{ backlink }}"/>
{{ if isset(cid) }}
<input type="hidden" name="cid" value="{{ cid }}"/>