Improvements on community flagged explicit photos
When a user marks that another photo should have been marked as explicit: * The owner of that photo gets a notification about it, which reminds them of the explicit photo policy. * The photo's "Flagged" boolean is set (along with the Explicit boolean) * The 'Edit' page on a Flagged photo shows a red banner above the Explicit option, explaining that it was flagged. The checkbox text is crossed-out, with a "no" cursor and title text over - but can still be unchecked. If the user removes the Explicit flag on a flagged photo and saves it: * An admin report is generated to notify to take a look too. * The Explicit flag is cleared as normal * The Flagged boolean is also cleared on this photo: if they set it back to Explicit again themselves, the red banner won't appear and it won't notify again - unless a community member flagged it again! Also makes some improvements to the admin page: * On photo reports: show a blurred-out (clickable to reveal) photo on feedback items about photos.
This commit is contained in:
parent
c8d9cdbb3a
commit
542d0bb300
|
@ -183,17 +183,30 @@ func Feedback() http.HandlerFunc {
|
|||
}
|
||||
|
||||
// Map user IDs.
|
||||
var userIDs = []uint64{}
|
||||
var (
|
||||
userIDs = []uint64{}
|
||||
photoIDs = []uint64{}
|
||||
)
|
||||
for _, p := range page {
|
||||
if p.UserID > 0 {
|
||||
userIDs = append(userIDs, p.UserID)
|
||||
}
|
||||
|
||||
if p.TableName == "photos" && p.TableID > 0 {
|
||||
photoIDs = append(photoIDs, p.TableID)
|
||||
}
|
||||
}
|
||||
userMap, err := models.MapUsers(currentUser, userIDs)
|
||||
if err != nil {
|
||||
session.FlashError(w, r, "Couldn't map user IDs: %s", err)
|
||||
}
|
||||
|
||||
// Map photo IDs.
|
||||
photoMap, err := models.MapPhotos(photoIDs)
|
||||
if err != nil {
|
||||
session.FlashError(w, r, "Couldn't map photo IDs: %s", err)
|
||||
}
|
||||
|
||||
var vars = map[string]interface{}{
|
||||
// Filter settings.
|
||||
"DistinctSubjects": models.DistinctFeedbackSubjects(),
|
||||
|
@ -205,6 +218,7 @@ func Feedback() http.HandlerFunc {
|
|||
"Acknowledged": acknowledged,
|
||||
"Feedback": page,
|
||||
"UserMap": userMap,
|
||||
"PhotoMap": photoMap,
|
||||
"Pager": pager,
|
||||
}
|
||||
if err := tmpl.Execute(w, r, vars); err != nil {
|
||||
|
|
|
@ -52,6 +52,7 @@ func MarkPhotoExplicit() http.HandlerFunc {
|
|||
}
|
||||
|
||||
photo.Explicit = true
|
||||
photo.Flagged = true
|
||||
if err := photo.Save(); err != nil {
|
||||
session.FlashError(w, r, "Couldn't save photo: %s", err)
|
||||
} else {
|
||||
|
|
|
@ -83,6 +83,7 @@ func MarkPhotoExplicit() http.HandlerFunc {
|
|||
}
|
||||
|
||||
photo.Explicit = true
|
||||
photo.Flagged = true
|
||||
if err := photo.Save(); err != nil {
|
||||
SendJSON(w, http.StatusBadRequest, Response{
|
||||
Error: fmt.Sprintf("Couldn't save the photo: %s", err),
|
||||
|
@ -90,6 +91,23 @@ func MarkPhotoExplicit() http.HandlerFunc {
|
|||
return
|
||||
}
|
||||
|
||||
// Send the photo's owner a notification so they are aware.
|
||||
if owner, err := models.GetUser(photo.UserID); err == nil {
|
||||
notif := &models.Notification{
|
||||
UserID: owner.ID,
|
||||
AboutUser: *owner,
|
||||
Type: models.NotificationExplicitPhoto,
|
||||
TableName: "photos",
|
||||
TableID: photo.ID,
|
||||
Link: fmt.Sprintf("/photo/view?id=%d", photo.ID),
|
||||
}
|
||||
if err := models.CreateNotification(notif); err != nil {
|
||||
log.Error("Couldn't create Likes notification: %s", err)
|
||||
}
|
||||
} else {
|
||||
log.Error("MarkExplicit: getting photo owner (%d): %s", photo.UserID, err)
|
||||
}
|
||||
|
||||
// If a non-admin user has hit this API, log an admin report for visibility and
|
||||
// to keep a pulse on things (e.g. in case of abuse).
|
||||
if !currentUser.IsAdmin {
|
||||
|
|
|
@ -88,6 +88,9 @@ func Edit() http.HandlerFunc {
|
|||
|
||||
// Are we GOING private?
|
||||
goingPrivate = visibility == models.PhotoPrivate && visibility != photo.Visibility
|
||||
|
||||
// Is the user fighting an 'Explicit' tag added by the community?
|
||||
isFightingExplicitFlag = photo.Flagged && photo.Explicit && !isExplicit
|
||||
)
|
||||
|
||||
if len(altText) > config.AltTextMaxLength {
|
||||
|
@ -141,6 +144,31 @@ func Edit() http.HandlerFunc {
|
|||
setProfilePic = false
|
||||
}
|
||||
|
||||
// If the user is fighting a recent Explicit flag from the community.
|
||||
if isFightingExplicitFlag {
|
||||
|
||||
// Notify the admin (unless we are an admin).
|
||||
if !requestUser.IsAdmin {
|
||||
fb := &models.Feedback{
|
||||
Intent: "report",
|
||||
Subject: "Explicit photo flag dispute",
|
||||
UserID: currentUser.ID,
|
||||
TableName: "photos",
|
||||
TableID: photo.ID,
|
||||
Message: "A user's photo was recently **flagged by the community** as Explicit, and its owner " +
|
||||
"has **removed** the Explicit setting.\n\n" +
|
||||
"Please check out the photo below and verify what its Explicit setting should be:",
|
||||
}
|
||||
|
||||
if err := models.CreateFeedback(fb); err != nil {
|
||||
log.Error("Couldn't save feedback from user updating their DOB: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Allow this change but clear the Flagged status.
|
||||
photo.Flagged = false
|
||||
}
|
||||
|
||||
if err := photo.Save(); err != nil {
|
||||
session.FlashError(w, r, "Couldn't save photo: %s", err)
|
||||
}
|
||||
|
|
|
@ -46,6 +46,7 @@ const (
|
|||
NotificationPrivatePhoto NotificationType = "private_photo" // private photo grants
|
||||
NotificationNewPhoto NotificationType = "new_photo"
|
||||
NotificationForumModerator NotificationType = "forum_moderator" // appointed as a forum moderator
|
||||
NotificationExplicitPhoto NotificationType = "explicit_photo" // user photo was flagged explicit
|
||||
NotificationCustom NotificationType = "custom" // custom message pushed
|
||||
)
|
||||
|
||||
|
|
|
@ -85,7 +85,13 @@ func (nf NotificationFilter) Query() (where string, placeholders []interface{},
|
|||
types = append(types, NotificationPrivatePhoto)
|
||||
}
|
||||
if nf.Misc {
|
||||
types = append(types, NotificationFriendApproved, NotificationCertApproved, NotificationCertRejected, NotificationCustom)
|
||||
types = append(types,
|
||||
NotificationFriendApproved,
|
||||
NotificationCertApproved,
|
||||
NotificationCertRejected,
|
||||
NotificationExplicitPhoto,
|
||||
NotificationCustom,
|
||||
)
|
||||
}
|
||||
|
||||
return "type IN ?", types, true
|
||||
|
|
|
@ -274,6 +274,54 @@ func GetOrphanedPhotos() ([]*Photo, int64, error) {
|
|||
return ps, count, res.Error
|
||||
}
|
||||
|
||||
// PhotoMap helps map a set of users to look up by ID.
|
||||
type PhotoMap map[uint64]*Photo
|
||||
|
||||
// MapPhotos looks up a set of photos IDs in bulk and returns a PhotoMap suitable for templates.
|
||||
func MapPhotos(photoIDs []uint64) (PhotoMap, error) {
|
||||
var (
|
||||
photoMap = PhotoMap{}
|
||||
set = map[uint64]interface{}{}
|
||||
distinct = []uint64{}
|
||||
)
|
||||
|
||||
// Uniqueify the IDs.
|
||||
for _, uid := range photoIDs {
|
||||
if _, ok := set[uid]; ok {
|
||||
continue
|
||||
}
|
||||
set[uid] = nil
|
||||
distinct = append(distinct, uid)
|
||||
}
|
||||
|
||||
var (
|
||||
photos = []*Photo{}
|
||||
result = DB.Model(&Photo{}).Where("id IN ?", distinct).Find(&photos)
|
||||
)
|
||||
|
||||
if result.Error == nil {
|
||||
for _, row := range photos {
|
||||
photoMap[row.ID] = row
|
||||
}
|
||||
}
|
||||
|
||||
return photoMap, result.Error
|
||||
}
|
||||
|
||||
// Has a photo ID in the map?
|
||||
func (pm PhotoMap) Has(id uint64) bool {
|
||||
_, ok := pm[id]
|
||||
return ok
|
||||
}
|
||||
|
||||
// Get a photo from the PhotoMap.
|
||||
func (pm PhotoMap) Get(id uint64) *Photo {
|
||||
if photo, ok := pm[id]; ok {
|
||||
return photo
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
/*
|
||||
IsSiteGalleryThrottled returns whether the user is throttled from marking additional pictures for the Site Gallery.
|
||||
|
||||
|
|
|
@ -16,6 +16,10 @@ abbr {
|
|||
cursor: default;
|
||||
}
|
||||
|
||||
.has-text-smaller {
|
||||
font-size: smaller;
|
||||
}
|
||||
|
||||
img {
|
||||
/* https://stackoverflow.com/questions/12906789/preventing-an-image-from-being-draggable-or-selectable-without-using-js */
|
||||
user-drag: none;
|
||||
|
|
|
@ -600,6 +600,11 @@
|
|||
You have been appointed as a <strong class="has-text-success">moderator</strong>
|
||||
for the forum <a href="/f/{{$Body.Forum.Fragment}}">{{$Body.Forum.Title}}</a>!
|
||||
</span>
|
||||
{{else if eq .Type "explicit_photo"}}
|
||||
<span class="icon"><i class="fa fa-fire has-text-danger"></i></span>
|
||||
<span>
|
||||
Your <a href="{{.Link}}">photo</a> was marked as <span class="has-text-danger">Explicit!</span>
|
||||
</span>
|
||||
{{else}}
|
||||
{{.AboutUser.Username}} {{.Type}} {{.TableName}} {{.TableID}}
|
||||
{{end}}
|
||||
|
@ -628,6 +633,18 @@
|
|||
<span class="icon"><i class="fa fa-arrow-right"></i></span>
|
||||
<a href="{{.Link}}">See all comments</a>
|
||||
</div>
|
||||
{{else if eq .Type "explicit_photo"}}
|
||||
<div class="content pt-1" style="font-size: smaller">
|
||||
<p>
|
||||
A community member thinks that this photo should have been marked as 'Explicit' when
|
||||
it was uploaded.
|
||||
</p>
|
||||
<p>
|
||||
Please <a href="/tos#explicit-photos">review our Explicit Photos policy</a>
|
||||
and remember to correctly mark your new uploads as 'explicit' when they contain sexually
|
||||
suggestive content.
|
||||
</p>
|
||||
</div>
|
||||
{{else}}
|
||||
<em>{{or $Body.Photo.Caption "No caption."}}</em>
|
||||
{{end}}
|
||||
|
|
|
@ -228,6 +228,19 @@
|
|||
{{else}}
|
||||
{{ToMarkdown .Message}}
|
||||
{{end}}
|
||||
|
||||
<!-- Photo attachment? -->
|
||||
{{if eq .TableName "photos"}}
|
||||
{{$Photo := $Root.PhotoMap.Get .TableID}}
|
||||
{{if $Photo}}
|
||||
<div class="is-clipped">
|
||||
<a href="{{$Root.Request.URL.Path}}?id={{.ID}}&visit=true">
|
||||
<img src="{{PhotoURL $Photo.Filename}}"
|
||||
class="blurred-explicit">
|
||||
</a>
|
||||
</div>
|
||||
{{end}}
|
||||
{{end}}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
|
|
@ -701,6 +701,12 @@
|
|||
content from other users -- by default this site is "normal nudes" friendly!
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Please see the <a href="/tos#explicit-photos">Explicit Photos & Sexual Content</a>
|
||||
policy on our <a href="/tos">Terms of Service</a> for some examples when a photo should
|
||||
be marked as 'explicit.'
|
||||
</p>
|
||||
|
||||
<h3 id="photoshop">Are digitally altered or 'photoshopped' pictures okay?</h3>
|
||||
|
||||
<p>
|
||||
|
|
|
@ -264,7 +264,6 @@
|
|||
<label class="label">
|
||||
<i class="fa fa-thumbtack mr-1 has-text-success"></i>
|
||||
Pinned Photo
|
||||
<span class="tag is-success ml-2">New!</span>
|
||||
</label>
|
||||
<label class="checkbox">
|
||||
<input type="checkbox"
|
||||
|
@ -391,6 +390,34 @@
|
|||
<span>Explicit Content</span>
|
||||
<span class="icon"><i class="fa fa-fire"></i></span>
|
||||
</label>
|
||||
|
||||
<!-- Flagged Explicit photo: show a warning if this photo was recently flagged by the community. -->
|
||||
{{$IsFlagged := and .EditPhoto .EditPhoto.Flagged .EditPhoto.Explicit}}
|
||||
{{if $IsFlagged}}
|
||||
<div class="notification is-danger is-light py-3 px-3 has-text-smaller content">
|
||||
<p>
|
||||
<strong>
|
||||
<i class="fa fa-exclamation-triangle"></i>
|
||||
Notice:
|
||||
</strong>
|
||||
This photo was classified by the community as containing 'Explicit' content.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Please review <a href="/tos#explicit-photos">what {{.Title}} considers an 'Explicit' photo</a>
|
||||
and if this photo fits the description, <strong>please</strong> leave this photo with the
|
||||
'Explicit' box checked, below.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
If you disagree that this photo should have been marked as 'Explicit,' you <strong>MAY</strong>
|
||||
uncheck the box below and remove the Explicit status. <strong>Note:</strong> the website admin will
|
||||
be notified to take a look as well if you do this, to verify that your photo has the correct 'Explicit'
|
||||
setting.
|
||||
</p>
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
{{if eq .Intent "profile_pic"}}
|
||||
<span class="has-text-danger">
|
||||
Your default profile picture should
|
||||
|
@ -405,12 +432,16 @@
|
|||
that to your page, just not as your default profile picture!
|
||||
</p>
|
||||
{{else}}
|
||||
<label class="checkbox">
|
||||
<label class="checkbox"
|
||||
{{if $IsFlagged}}title="You MAY remove this check if you disagree that this photo should be marked Explicit"{{end}}
|
||||
>
|
||||
<input type="checkbox"
|
||||
name="explicit"
|
||||
value="true"
|
||||
{{if .EditPhoto.Explicit}}checked{{end}}>
|
||||
{{if $IsFlagged}}<del class="cursor-not-allowed">{{end}}
|
||||
This photo contains explicit content
|
||||
{{if $IsFlagged}}</del>{{end}}
|
||||
</label>
|
||||
<p class="help">
|
||||
Mark this box if this photo contains any explicit content, including an
|
||||
|
|
|
@ -380,20 +380,20 @@
|
|||
</p>
|
||||
|
||||
<p>
|
||||
A photo is considered "explicit" if it depicts <em>any</em> of the following features:
|
||||
A photo is considered "explicit" if it depicts <strong>any</strong> of the following features:
|
||||
</p>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
A close-up view of genitalia or where the genitals are the central focus of the picture.
|
||||
</li>
|
||||
<li>An erect penis if the subject has one, especially if they are grabbing it.</li>
|
||||
<li>An erect or semi-erect penis if the subject has one, especially if they are grabbing it.</li>
|
||||
<li>
|
||||
"Spread eagle" pictures that clearly and especially show intimate body parts such
|
||||
as butt holes or vulvae.
|
||||
</li>
|
||||
<li>
|
||||
A depiction of a sexual act, including but not limited to: masturbation, oral sex,
|
||||
A depiction of any sexual activity, including but not limited to: masturbation, oral sex,
|
||||
anal or vaginal penetration, humping, or any content intended to sexually arouse the
|
||||
viewer. If it can be reasonably considered to be "porn" it is an explicit photo.
|
||||
</li>
|
||||
|
@ -405,6 +405,17 @@
|
|||
</li>
|
||||
</ul>
|
||||
|
||||
<p>
|
||||
As a general rule of thumb: if a picture could be reasonably considered to be "porn" then you should
|
||||
mark it as Explicit when uploading it to your gallery.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<strong>Important:</strong> extreme and commonly offensive content (such as fisting, gaping or prolapsed
|
||||
ass holes, etc.) are <strong>NOT</strong> permitted on {{PrettyTitle}}. Please review the following
|
||||
section for a list of Prohibited Content.
|
||||
</p>
|
||||
|
||||
<h3 id="prohibited-content">Prohibited Content <a href="#prohibited-content" class="fa fa-paragraph is-size-6"></a></h3>
|
||||
|
||||
<p>
|
||||
|
@ -414,6 +425,7 @@
|
|||
|
||||
<ul>
|
||||
<li>
|
||||
<strong>Illegal content:</strong>
|
||||
You may NOT upload any content that is considered to be illegal in the United States or
|
||||
in any of the 50 States therein. This includes, but is not limited to: bestiality (or
|
||||
sexual acts involving animals), child sexually abusive material (CSAM), ANY nude photo
|
||||
|
@ -423,6 +435,7 @@
|
|||
or other unlawful content.
|
||||
</li>
|
||||
<li>
|
||||
<strong>Extreme content:</strong>
|
||||
You may NOT upload sexual material depicting extreme or commonly offensive content
|
||||
including, but not limited to: watersports (peeing onto or into another person), scat
|
||||
(any depiction of obviously apparent fecal matter), prolapsed rectum, anal fisting,
|
||||
|
|
Loading…
Reference in New Issue
Block a user