Blur Explicit GIFs + Forum Improvements

* Fix a bug where explicit GIF videos weren't blurring when the user
  wanted them to be blurred.
* Make some improvements to the forums:
  * Polls will now remember the Expires setting while you are previewing
    and revising your original post.
  * Add 14 day and 30 day Expire options for polls.
  * Add disclaimer warnings above the Photo Attachment field when the
    current forum or thread isn't marked for Explicit content.
This commit is contained in:
Noah Petherbridge 2024-11-28 11:21:06 -08:00
parent f7e5e36576
commit 688d2e0baa
7 changed files with 160 additions and 42 deletions

View File

@ -165,6 +165,50 @@ var (
"Anything Goes", "Anything Goes",
} }
// Forum Poll expiration options.
PollExpires = []Option{
{
Label: "Never",
Value: "0",
},
{
Label: "1 Day",
Value: "1",
},
{
Label: "2 Days",
Value: "2",
},
{
Label: "3 Days",
Value: "3",
},
{
Label: "4 Days",
Value: "4",
},
{
Label: "5 Days",
Value: "5",
},
{
Label: "6 Days",
Value: "6",
},
{
Label: "7 Days",
Value: "7",
},
{
Label: "2 Weeks",
Value: "14",
},
{
Label: "1 Month (30 days)",
Value: "30",
},
}
// Keywords that appear in a DM that make it likely spam. // Keywords that appear in a DM that make it likely spam.
DirectMessageSpamKeywords = []*regexp.Regexp{ DirectMessageSpamKeywords = []*regexp.Regexp{
regexp.MustCompile(`\b(telegram|whats\s*app|signal|kik|session)\b`), regexp.MustCompile(`\b(telegram|whats\s*app|signal|kik|session)\b`),

View File

@ -47,6 +47,10 @@ func NewPost() http.HandlerFunc {
// well (pinned, explicit, noreply) // well (pinned, explicit, noreply)
isOriginalComment bool isOriginalComment bool
// If neither the forum nor thread are explicit, show a hint to the user not to
// share an explicit photo in their reply.
explicitPhotoAllowed bool
// Polls // Polls
pollOptions = []string{} pollOptions = []string{}
pollExpires = 3 pollExpires = 3
@ -87,6 +91,11 @@ func NewPost() http.HandlerFunc {
} }
} }
// Would an explicit photo attachment be allowed?
if forum.Explicit || (thread != nil && thread.Explicit) {
explicitPhotoAllowed = true
}
// If the current user can moderate the forum thread, e.g. edit or delete posts. // If the current user can moderate the forum thread, e.g. edit or delete posts.
// Admins can edit always, user owners of forums can only delete. // Admins can edit always, user owners of forums can only delete.
var canModerate = currentUser.HasAdminScope(config.ScopeForumModerator) || var canModerate = currentUser.HasAdminScope(config.ScopeForumModerator) ||
@ -499,6 +508,7 @@ func NewPost() http.HandlerFunc {
"PostTitle": title, "PostTitle": title,
"EditCommentID": editCommentID, "EditCommentID": editCommentID,
"EditThreadSettings": isOriginalComment, "EditThreadSettings": isOriginalComment,
"ExplicitPhotoAllowed": explicitPhotoAllowed,
"Message": message, "Message": message,
// Thread settings (for editing the original comment esp.) // Thread settings (for editing the original comment esp.)
@ -508,6 +518,8 @@ func NewPost() http.HandlerFunc {
// Polls // Polls
"PollOptions": pollOptions, "PollOptions": pollOptions,
"PollExpires": pollExpires,
"PollExpiresOptions": config.PollExpires,
// Attached photo. // Attached photo.
"CommentPhoto": commentPhoto, "CommentPhoto": commentPhoto,

View File

@ -20,6 +20,10 @@ func Thread() http.HandlerFunc {
idStr = r.PathValue("id") idStr = r.PathValue("id")
forum *models.Forum forum *models.Forum
thread *models.Thread thread *models.Thread
// If neither the forum nor thread are explicit, show a hint to the user not to
// share an explicit photo in their reply.
explicitPhotoAllowed bool
) )
if idStr == "" { if idStr == "" {
@ -61,6 +65,11 @@ func Thread() http.HandlerFunc {
// e.g. can we delete threads and posts, not edit them) // e.g. can we delete threads and posts, not edit them)
var canModerate = forum.CanBeModeratedBy(currentUser) var canModerate = forum.CanBeModeratedBy(currentUser)
// Would an explicit photo attachment be allowed?
if forum.Explicit || thread.Explicit {
explicitPhotoAllowed = true
}
// Ping the view count on this thread. // Ping the view count on this thread.
if err := thread.View(currentUser.ID); err != nil { if err := thread.View(currentUser.ID); err != nil {
log.Error("Couldn't ping view count on thread %d: %s", thread.ID, err) log.Error("Couldn't ping view count on thread %d: %s", thread.ID, err)
@ -114,6 +123,7 @@ func Thread() http.HandlerFunc {
"CanModerate": canModerate, "CanModerate": canModerate,
"IsSubscribed": isSubscribed, "IsSubscribed": isSubscribed,
"IsForumSubscribed": models.IsForumSubscribed(currentUser, forum), "IsForumSubscribed": models.IsForumSubscribed(currentUser, forum),
"ExplicitPhotoAllowed": explicitPhotoAllowed,
} }
if err := tmpl.Execute(w, r, vars); err != nil { if err := tmpl.Execute(w, r, vars); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError) http.Error(w, err.Error(), http.StatusInternalServerError)

View File

@ -38,6 +38,9 @@ func TemplateFuncs(r *http.Request) template.FuncMap {
"ToMarkdown": ToMarkdown, "ToMarkdown": ToMarkdown,
"ToJSON": ToJSON, "ToJSON": ToJSON,
"ToHTML": ToHTML, "ToHTML": ToHTML,
"ToString": func(v interface{}) string {
return fmt.Sprintf("%v", v)
},
"PhotoURL": PhotoURL(r), "PhotoURL": PhotoURL(r),
"VisibleAvatarURL": photo.VisibleAvatarURL, "VisibleAvatarURL": photo.VisibleAvatarURL,
"Now": time.Now, "Now": time.Now,

View File

@ -28,6 +28,8 @@
</div> </div>
</section> </section>
{{$Root := .}}
<div class="block p-4"> <div class="block p-4">
<div class="columns is-centered"> <div class="columns is-centered">
<div class="column is-half"> <div class="column is-half">
@ -83,7 +85,7 @@
{{if and (not .EditCommentID) (not .Thread)}} {{if and (not .EditCommentID) (not .Thread)}}
<div class="field block"> <div class="field block">
<label for="message" class="label">Poll <span class="tag is-success">NEW!</span></label> <label for="message" class="label">Poll</label>
<div v-if="pollVisible"> <div v-if="pollVisible">
<!-- MultipleChoice option --> <!-- MultipleChoice option -->
<div class="mb-2"> <div class="mb-2">
@ -134,14 +136,9 @@
<div class="select is-small is-fullwidth"> <div class="select is-small is-fullwidth">
<select v-model="expires" <select v-model="expires"
name="poll_expires"> name="poll_expires">
<option :value="0">Never</option> {{range .PollExpiresOptions}}
<option :value="1">1 Day</option> <option :value="{{.Value}}">{{.Label}}</option>
<option :value="2">2 Days</option> {{end}}
<option :value="3">3 Days</option>
<option :value="4">4 Days</option>
<option :value="5">5 Days</option>
<option :value="6">6 Days</option>
<option :value="7">7 Days</option>
</select> </select>
</div> </div>
</div> </div>
@ -173,6 +170,33 @@
<div class="field block"> <div class="field block">
<label class="label">Photo Attachment</label> <label class="label">Photo Attachment</label>
<!-- Non-explicit Photo Notice -->
{{if not .ExplicitPhotoAllowed}}
<p class="help mb-4">
<!-- If NEW thread on a non-explicit forum -->
{{if not .Thread}}
<strong class="has-text-danger">
<i class="fa fa-exclamation-triangle mr-1"></i>
Explicit Photo Notice:
</strong>
The forum you are posting to isn't marked as Explicit. If you are going to share
an <a href="/faq#define-explicit">Explicit</a> photo, <strong>please also mark this thread
as Explicit</strong> using the checkbox below!
{{else}}
<strong class="has-text-danger">
<i class="fa fa-ban mr-1"></i>
No Explicit Photos:
</strong>
This {{if not .Thread}}forum{{else}}thread{{end}} is not marked to accept Explicit
photos being shared. If you are going to upload a nude picture, <strong>please make sure</strong>
it is not an <a href="/faq#define-explicit">Explicit</a> photo. Thanks!
{{end}}
</p>
{{end}}
<div class="file has-name is-fullwidth"> <div class="file has-name is-fullwidth">
<label class="file-label"> <label class="file-label">
<input class="file-input" type="file" <input class="file-input" type="file"
@ -238,6 +262,13 @@
{{if .IsExplicit}}checked{{end}}> {{if .IsExplicit}}checked{{end}}>
Mark as Explicit (NSFW) Mark as Explicit (NSFW)
</label> </label>
{{if not .Forum.Explicit}}
<p class="help">
The forum you are posting this in is not marked as Explicit, however the occasional
Explicit thread is allowed as long as it is correctly tagged. <strong>Please</strong>
check this box if the text or attached photos on this thread will be sexual in nature!
</p>
{{end}}
</div> </div>
{{end}} {{end}}
@ -376,7 +407,7 @@
{value: ""}, {value: ""},
{value: ""}, {value: ""},
], ],
expires: 3, // days expires: parseInt({{.PollExpires}}), // days
answersLimit: 100, answersLimit: 100,
} }
}, },

View File

@ -264,9 +264,14 @@
{{else}} {{else}}
<!-- GIF video? --> <!-- GIF video? -->
{{if HasSuffix .Filename ".mp4"}} {{if HasSuffix .Filename ".mp4"}}
<video autoplay loop controls controlsList="nodownload" playsinline> <div class="mt-4 is-clipped">
<video loop controls controlsList="nodownload" playsinline
{{if and (or $Root.Forum.Explicit $Root.Thread.Explicit) (eq ($Root.CurrentUser.GetProfileField "blur_explicit") "true")}} class="blurred-explicit"{{end}}
{{if and (not (eq ($Root.CurrentUser.GetProfileField "blur_explicit") "true")) (not (eq ($Root.CurrentUser.GetProfileField "autoplay_gif") "false"))}}autoplay{{end}}
>
<source src="{{PhotoURL .Filename}}" type="video/mp4"> <source src="{{PhotoURL .Filename}}" type="video/mp4">
</video> </video>
</div>
{{else}} {{else}}
<div class="mt-4 is-clipped"> <div class="mt-4 is-clipped">
<img src="{{PhotoURL .Filename}}" loading="lazy"{{if and (or $Root.Forum.Explicit $Root.Thread.Explicit) (eq ($Root.CurrentUser.GetProfileField "blur_explicit") "true")}} class="blurred-explicit"{{end}}> <img src="{{PhotoURL .Filename}}" loading="lazy"{{if and (or $Root.Forum.Explicit $Root.Thread.Explicit) (eq ($Root.CurrentUser.GetProfileField "blur_explicit") "true")}} class="blurred-explicit"{{end}}>
@ -485,6 +490,21 @@
</div> </div>
<label class="label">Photo Attachment</label> <label class="label">Photo Attachment</label>
<!-- Non-explicit Photo Notice -->
{{if not .ExplicitPhotoAllowed}}
<p class="help mb-4">
<strong class="has-text-danger">
<i class="fa fa-ban mr-1"></i>
No Explicit Photos:
</strong>
This {{if not .Thread}}forum{{else}}thread{{end}} is not marked to accept Explicit
photos being shared. If you are going to upload a nude picture, <strong>please make sure</strong>
it is not an <a href="/faq#define-explicit">Explicit</a> photo. Thanks!
</p>
{{end}}
<div class="file has-name is-fullwidth"> <div class="file has-name is-fullwidth">
<label class="file-label"> <label class="file-label">
<input class="file-input" type="file" <input class="file-input" type="file"

View File

@ -553,12 +553,11 @@
<!-- GIF video? --> <!-- GIF video? -->
{{if HasSuffix .Filename ".mp4"}} {{if HasSuffix .Filename ".mp4"}}
<video loop controls controlsList="nodownload" playsinline <video loop controls controlsList="nodownload" playsinline
class="js-modal-trigger" class="js-modal-trigger{{if BlurExplicit .}} blurred-explicit{{end}}"
data-url="{{PhotoURL .Filename}}" data-photo-id="{{.ID}}" data-url="{{PhotoURL .Filename}}" data-photo-id="{{.ID}}"
{{if .AltText}}title="{{.AltText}}"{{end}} {{if .AltText}}title="{{.AltText}}"{{end}}
{{if BlurExplicit .}}class="blurred-explicit" {{if and (not (BlurExplicit .)) (not (eq ($Root.CurrentUser.GetProfileField "autoplay_gif") "false"))}}autoplay{{end}}
{{else if (not (eq ($Root.CurrentUser.GetProfileField "autoplay_gif") "false"))}}autoplay >
{{end}}>
<source src="{{PhotoURL .Filename}}" type="video/mp4"> <source src="{{PhotoURL .Filename}}" type="video/mp4">
</video> </video>
{{else}} {{else}}
@ -680,12 +679,11 @@
<!-- GIF video? --> <!-- GIF video? -->
{{if HasSuffix .Filename ".mp4"}} {{if HasSuffix .Filename ".mp4"}}
<video loop controls controlsList="nodownload" playsinline <video loop controls controlsList="nodownload" playsinline
class="js-modal-trigger" class="js-modal-trigger{{if BlurExplicit .}} blurred-explicit{{end}}"
data-url="{{PhotoURL .Filename}}" data-photo-id="{{.ID}}" data-url="{{PhotoURL .Filename}}" data-photo-id="{{.ID}}"
{{if .AltText}}title="{{.AltText}}"{{end}} {{if .AltText}}title="{{.AltText}}"{{end}}
{{if BlurExplicit .}}class="blurred-explicit" {{if and (not (BlurExplicit .)) (not (eq ($Root.CurrentUser.GetProfileField "autoplay_gif") "false"))}}autoplay{{end}}
{{else if (not (eq ($Root.CurrentUser.GetProfileField "autoplay_gif") "false"))}}autoplay >
{{end}}>
<source src="{{PhotoURL .Filename}}" type="video/mp4"> <source src="{{PhotoURL .Filename}}" type="video/mp4">
</video> </video>
{{else}} {{else}}