basic behavior of filtering users added

This commit is contained in:
2026-01-28 22:56:17 -07:00
parent b59e15a48e
commit 17471f292a
6 changed files with 133 additions and 30 deletions
+28 -3
View File
@@ -405,6 +405,15 @@ func templateAttachmentInfo(args jet.Arguments) reflect.Value {
return reflect.ValueOf(rc)
}
// templateBozo returns true if the post's creator user has been filtered by the current one.
func templateBozo(args jet.Arguments) reflect.Value {
post := args.Get(0).Convert(reflect.TypeFor[*database.PostHeader]()).Interface().(*database.PostHeader)
topic := args.Get(1).Convert(reflect.TypeFor[*database.Topic]()).Interface().(*database.Topic)
ctxt := args.Get(2).Convert(reflect.TypeFor[ui.AmContext]()).Interface().(ui.AmContext)
rc, _ := topic.IsBozo(ctxt.Ctx(), ctxt.CurrentUser(), post.CreatorUid)
return reflect.ValueOf(rc)
}
/* ReadPosts displays posts in a topic.
* Parameters:
* ctxt - The AmContext for the request.
@@ -450,6 +459,7 @@ func ReadPosts(ctxt ui.AmContext) (string, any, error) {
conf := ctxt.GetScratch("currentConference").(*database.Conference)
myLevel := ctxt.GetScratch("levelInConference").(uint16)
topic := ctxt.GetScratch("currentTopic").(*database.Topic)
ctxt.VarMap().Set("post_topic", topic)
// Determine the range of posts to display. The "pin" is the post number after which we display the horizontal line separating old and new posts.
lastRead, err := topic.GetLastRead(ctxt.Ctx(), ctxt.CurrentUser())
@@ -544,6 +554,13 @@ func ReadPosts(ctxt ui.AmContext) (string, any, error) {
// Set advanced controls.
advancedControls := ctxt.HasParameter("ac") && (len(posts) == 1)
if advancedControls {
nbozo := ctxt.QueryParamInt("bozo", -1)
if nbozo >= 0 {
err = topic.SetBozo(ctxt.Ctx(), ctxt.CurrentUser(), posts[0].CreatorUid, nbozo != 0)
if err != nil {
return ui.ErrorPage(ctxt, err)
}
}
isMyPost := (posts[0].CreatorUid == ctxt.CurrentUserId()) && !ctxt.CurrentUser().IsAnon
isScribbled := posts[0].IsScribbled()
canHide := !isScribbled && (isMyPost || confHidePerm)
@@ -560,9 +577,6 @@ func ReadPosts(ctxt ui.AmContext) (string, any, error) {
}
}
ctxt.VarMap().Set("canPublish", canPublish)
if !canHide && !canScribble && !confNukePerm && !canPublish {
advancedControls = false
}
}
ctxt.VarMap().Set("advancedControls", advancedControls)
@@ -588,6 +602,7 @@ func ReadPosts(ctxt ui.AmContext) (string, any, error) {
ctxt.VarMap().SetFunc("post_getText", templatePostText)
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("%s/r/%d", urlStem, topic.Number))
ctxt.VarMap().Set("post_max", topic.TopMessage)
ctxt.VarMap().Set("posts", posts)
@@ -605,12 +620,21 @@ func ReadPosts(ctxt ui.AmContext) (string, any, error) {
return "framed_template", "posts.jet", nil
}
/* PostInTopic adds a new post to a topic.
* 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 PostInTopic(ctxt ui.AmContext) (string, any, error) {
// Locate community, conference, and topic.
comm := ctxt.CurrentCommunity()
conf := ctxt.GetScratch("currentConference").(*database.Conference)
level := ctxt.GetScratch("levelInConference").(uint16)
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)
if ctxt.FormFieldIsSet("cancel") {
@@ -711,6 +735,7 @@ func PostInTopic(ctxt ui.AmContext) (string, any, error) {
ctxt.VarMap().SetFunc("post_getText", templatePostText)
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)
+68
View File
@@ -129,6 +129,74 @@ func (t *Topic) SetHidden(ctx context.Context, u *User, hidden bool) error {
return err
}
// IsBozo returns true if the specified test UID is filtered for the specified user.
func (t *Topic) IsBozo(ctx context.Context, u *User, testUid int32) (bool, error) {
if u.IsAnon {
return false, nil
}
row := amdb.QueryRowContext(ctx, "SELECT bozo_uid FROM topicbozo WHERE topicid = ? AND uid = ? AND bozo_uid = ?", t.TopicId, u.Uid, testUid)
var tmp int32
err := row.Scan(&tmp)
switch err {
case nil:
return true, nil
case sql.ErrNoRows:
return false, nil
}
return false, err
}
// SetBozo adds or removes a filter of a subject UID for the specified user.
func (t *Topic) SetBozo(ctx context.Context, u *User, subjectUid int32, bozo bool) error {
var err error = nil
if !u.IsAnon {
if bozo { // Flipping the bozo bit!
row := amdb.QueryRowContext(ctx, "SELECT bozo_uid FROM topicbozo WHERE topicid = ? AND uid = ? AND bozo_uid = ?", t.TopicId, u.Uid, subjectUid)
var tmp int32
err = row.Scan(&tmp)
switch err {
case nil:
return nil
case sql.ErrNoRows:
_, err = amdb.ExecContext(ctx, "INSERT INTO topicbozo (topicid, uid, bozo_uid) VALUES (?, ?, ?)", t.TopicId, u.Uid, subjectUid)
}
} else {
_, err = amdb.ExecContext(ctx, "DELETE FROM topicbozo WHERE topicid = ? AND uid = ? AND bozo_uid = ?", t.TopicId, u.Uid, subjectUid)
}
}
return err
}
// TopicBozo is a structure that returns all information about a filtered user.
type TopicBozo struct {
Uid int32
Username string
GivenName string
FamilyName string
}
// GetBozos returns all filtered users for a given user on the topic.
func (t *Topic) GetBozos(ctx context.Context, u *User) ([]TopicBozo, error) {
if u.IsAnon {
return make([]TopicBozo, 0), nil
}
rs, err := amdb.QueryContext(ctx, `SELECT b.bozo_uid, u.username, c.given_name, c.family_name
FROM topicbozo b, users u, contacts c WHERE b.topicid = ? AND b.uid = ? AND b.bozo_uid = u.uid AND u.contactid = c.contactid`, t.TopicId, u.Uid)
if err != nil {
return nil, err
}
rc := make([]TopicBozo, 0)
for rs.Next() {
var tb TopicBozo
err = rs.Scan(&(tb.Uid), &(tb.Username), &(tb.GivenName), &(tb.FamilyName))
if err != nil {
return nil, err
}
rc = append(rc, tb)
}
return rc, nil
}
// TopicSettings contains per-user settings for topics, including the "last read" message pointer.
type TopicSettings struct {
TopicId int32 `db:"topicid"` // unique ID of the topic
+1 -1
View File
@@ -42,7 +42,7 @@ _(italicized items can be deferred)_
- Delete
- ~~Post Scribble~~
- ~~Post Nuke~~
- Post Filter User
- ~~Post Filter User~~
- Post Move
- Post Publish
- Manage Communities on communities sidebox
+4 -2
View File
@@ -109,6 +109,7 @@
{{ post_overrideLine := "" }}
{{ post_overrideLink := "" }}
{{ post_attach := nil }}
{{ post_bozo := false }}
{{ range i, p := posts }}
{{ post_cur = p }}
{{ post_userName = post_getUserName(p, .) }}
@@ -116,6 +117,7 @@
{{ post_overrideLine = post_getOverrideLine(p, .) }}
{{ post_overrideLink = post_getOverrideLink(p, post_topicPermalink) }}
{{ post_attach = post_getAttachmentInfo(p, .) }}
{{ post_bozo = post_isBozo(p, post_topic, .) }}
{{ include "singlepost.jet" }}
{{ if advancedControls }}
<div class="flex flex-col gap-2">
@@ -129,8 +131,8 @@
<a href="{{ topicListLink }}/op/{{ topicNum }}/scribble/{{ p.Num }}"
class="bg-yellow-600 hover:bg-yellow-700 text-white px-3 py-2 rounded text-sm font-medium transition-colors whitespace-nowrap">Scribble</a>
{{ end }}
{{ if false }}{* TODO *}
<a href="/TODO"
{{ if !isAnon }}
<a href="{{ post_stem }}?r={{ p.Num }}&ac=1&bozo=1"
class="bg-blue-600 hover:bg-blue-700 text-white px-3 py-2 rounded text-sm font-medium transition-colors whitespace-nowrap">Filter User</a>
{{ end }}
{{ if canNuke }}
+8
View File
@@ -15,6 +15,13 @@
title="Permalink to this post">🔗&lt;{{ post_confRef }}.{{ post_cur.Num }}&gt;</a>
</div>
</div>
{{ if post_bozo }}
<div class="mt-4 mb-2">
<span class="italic font-bold">
(User filtered; <a class="text-blue-700 hover:text-blue-900" href="{{ post_stem }}?r={{ post_cur.Num }}&ac=1&bozo=0">remove filter</a>)
</span>
</div>
{{ else }}
<div class="mb-2">
<strong class="text-lg">{{ post_cur.Pseud | raw }}</strong>
<span class="text-gray-600 text-sm ml-2">(<em>
@@ -40,4 +47,5 @@
{{ else }}
<pre class="amsPost font-mono text-sm whitespace-pre-wrap bg-gray-50 p-4 rounded border border-gray-200">{{ post_text | postRewrite | raw }}</pre>
{{ end }}
{{ end }}
</div>
+2 -2
View File
@@ -25,15 +25,15 @@
{{ post_overrideLine := "" }}
{{ post_overrideLink := "" }}
{{ post_attach := nil }}
{{ post_bozo := false }}
{{ range i, p := posts }}
{{ m = map("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_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_attach = post_getAttachmentInfo(p, .) }}
{{ post_bozo = post_isBozo(p, post_topic, .) }}
{{ include "singlepost.jet" }}
{{ end }}
</div>