diff --git a/conference.go b/conference.go index dac1910..ba120e8 100644 --- a/conference.go +++ b/conference.go @@ -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) diff --git a/database/topic.go b/database/topic.go index 10b8a80..f9a839a 100644 --- a/database/topic.go +++ b/database/topic.go @@ -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 diff --git a/docs/MISSINGFUNCS.md b/docs/MISSINGFUNCS.md index 122046d..a5c98a0 100644 --- a/docs/MISSINGFUNCS.md +++ b/docs/MISSINGFUNCS.md @@ -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 diff --git a/ui/views/posts.jet b/ui/views/posts.jet index ad8ba50..5b54784 100644 --- a/ui/views/posts.jet +++ b/ui/views/posts.jet @@ -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 }}
@@ -129,8 +131,8 @@ Scribble {{ end }} - {{ if false }}{* TODO *} - Filter User {{ end }} {{ if canNuke }} diff --git a/ui/views/singlepost.jet b/ui/views/singlepost.jet index 6ca02ae..43352d6 100644 --- a/ui/views/singlepost.jet +++ b/ui/views/singlepost.jet @@ -15,29 +15,37 @@ title="Permalink to this post">🔗<{{ post_confRef }}.{{ post_cur.Num }}>
-
- {{ post_cur.Pseud | raw }} - ( - {{ post_userName }}, - {{ DisplayDateTime(post_cur.Posted, .) }}) - {{ if post_attach.Filename != "" }} - 📎 - {{ end }} - -
- {{ if post_overrideLine != "" }} -
- {{ if post_overrideLink != "" }} - {{ post_overrideLine }} - {{ else }} - {{ post_overrideLine }} - {{ end }} + {{ if post_bozo }} +
+ + (User filtered; remove filter) +
{{ else }} -
{{ post_text | postRewrite | raw }}
+
+ {{ post_cur.Pseud | raw }} + ( + {{ post_userName }}, + {{ DisplayDateTime(post_cur.Posted, .) }}) + {{ if post_attach.Filename != "" }} + 📎 + {{ end }} + +
+ {{ if post_overrideLine != "" }} +
+ {{ if post_overrideLink != "" }} + {{ post_overrideLine }} + {{ else }} + {{ post_overrideLine }} + {{ end }} +
+ {{ else }} +
{{ post_text | postRewrite | raw }}
+ {{ end }} {{ end }}
diff --git a/ui/views/slippage.jet b/ui/views/slippage.jet index c244405..f8be8a2 100644 --- a/ui/views/slippage.jet +++ b/ui/views/slippage.jet @@ -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 }}