website/pkg/controller/photo/user_gallery.go
Noah Petherbridge 666d3105b7 Privacy Improvements and Notification Fixes
* On user profile pages and gallery: the total photo count for the user
  will only include photos that the viewer can actually see (taking into
  account friendship and private grants), so that users won't harass
  each other to see the additional photos that aren't visible to them.
* On the member directory search: the photo counts will only show public
  photos on their page for now, and may be fewer than the number of
  photos the current user could actually see.
* Blocklist: you can now manually add a user by username to your block
  list. So if somebody blocked you on the site and you want to block
  them back, there is a way to do this.
* Friends: you can now directly unfriend someone from their profile
  page by clicking on the "Friends" button. You get a confirmation
  popup before the remove friend action goes through.
* Bugfix: when viewing a user's gallery, you were able to see their
  Friends-only photos if they granted you their Private photo access,
  even if you were not their friend.
* Bugfix: when uploading a new private photo, instead of notifying
  everybody you granted access to your privates it will only notify
  if they are also on your friend list.
2023-08-14 18:50:34 -07:00

166 lines
5.3 KiB
Go

package photo
import (
"net/http"
"regexp"
"code.nonshy.com/nonshy/website/pkg/config"
"code.nonshy.com/nonshy/website/pkg/log"
"code.nonshy.com/nonshy/website/pkg/models"
"code.nonshy.com/nonshy/website/pkg/session"
"code.nonshy.com/nonshy/website/pkg/templates"
)
var UserPhotosRegexp = regexp.MustCompile(`^/photo/u/([^@]+?)$`)
// UserPhotos controller (/photo/u/:username) to view a user's gallery or manage if it's yourself.
func UserPhotos() http.HandlerFunc {
tmpl := templates.Must("photo/gallery.html")
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Query params.
var (
viewStyle = r.FormValue("view") // cards (default), full
)
if viewStyle != "full" {
viewStyle = "cards"
}
// Parse the username out of the URL parameters.
var username string
m := UserPhotosRegexp.FindStringSubmatch(r.URL.Path)
if m != nil {
username = m[1]
}
// Find this user.
user, err := models.FindUser(username)
if err != nil {
templates.NotFoundPage(w, r)
return
}
// Load the current user in case they are viewing their own page.
currentUser, err := session.CurrentUser(r)
if err != nil {
session.FlashError(w, r, "Unexpected error: couldn't get CurrentUser")
}
var (
isOwnPhotos = currentUser.ID == user.ID
isShy = currentUser.IsShy()
isShyFrom = !isOwnPhotos && (currentUser.IsShyFrom(user) || (isShy && !models.AreFriends(currentUser.ID, user.ID)))
)
// Bail early if we are shy from this user.
if isShy && isShyFrom {
var vars = map[string]interface{}{
"IsOwnPhotos": currentUser.ID == user.ID,
"IsShyUser": isShy,
"IsShyFrom": isShyFrom,
// "IsMyPrivateUnlockedFor": isGranted, // have WE granted THIS USER to see our private pics?
// "AreWeGrantedPrivate": isGrantee, // have THEY granted US private photo access.
"User": user,
"Photos": []*models.Photo{},
"PhotoCount": models.CountPhotos(user.ID),
"Pager": models.Pagination{},
}
if err := tmpl.Execute(w, r, vars); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
return
}
// Is either one blocking?
if models.IsBlocking(currentUser.ID, user.ID) && !currentUser.IsAdmin {
templates.NotFoundPage(w, r)
return
}
// Is this user private and we're not friends?
var (
areFriends = models.AreFriends(user.ID, currentUser.ID)
isPrivate = user.Visibility == models.UserVisibilityPrivate && !areFriends
)
if isPrivate && !currentUser.IsAdmin && !isOwnPhotos {
session.FlashError(w, r, "This user's profile page and photo gallery are private.")
templates.Redirect(w, "/u/"+user.Username)
return
}
// Has this user granted access to see their privates?
var (
isGrantee = models.IsPrivateUnlocked(user.ID, currentUser.ID) // THEY have granted US access
isGranted = models.IsPrivateUnlocked(currentUser.ID, user.ID) // WE have granted THEM access
)
// What set of visibilities to query?
visibility := []models.PhotoVisibility{models.PhotoPublic}
if isOwnPhotos || isGrantee || currentUser.HasAdminScope(config.ScopePhotoModerator) {
visibility = append(visibility, models.PhotoPrivate)
}
if models.AreFriends(user.ID, currentUser.ID) {
visibility = append(visibility, models.PhotoFriends)
}
// Inner circle photos.
if currentUser.IsInnerCircle() {
visibility = append(visibility, models.PhotoInnerCircle)
}
// Explicit photo filter?
explicit := currentUser.Explicit
if isOwnPhotos {
explicit = true
}
// Get the page of photos.
pager := &models.Pagination{
Page: 1,
PerPage: config.PageSizeUserGallery,
Sort: "created_at desc",
}
pager.ParsePage(r)
photos, err := models.PaginateUserPhotos(user.ID, visibility, explicit, pager)
if err != nil {
log.Error("PaginateUserPhotos(%s): %s", user.Username, err)
}
// Get the count of explicit photos if we are not viewing explicit photos.
var explicitCount int64
if !explicit {
explicitCount, _ = models.CountExplicitPhotos(user.ID, visibility)
}
// Get Likes information about these photos.
var photoIDs = []uint64{}
for _, p := range photos {
photoIDs = append(photoIDs, p.ID)
}
likeMap := models.MapLikes(currentUser, "photos", photoIDs)
commentMap := models.MapCommentCounts("photos", photoIDs)
var vars = map[string]interface{}{
"IsOwnPhotos": currentUser.ID == user.ID,
"IsShyUser": isShy,
"IsShyFrom": isShyFrom,
"IsMyPrivateUnlockedFor": isGranted, // have WE granted THIS USER to see our private pics?
"AreWeGrantedPrivate": isGrantee, // have THEY granted US private photo access.
"User": user,
"Photos": photos,
"PhotoCount": models.CountPhotosICanSee(user, currentUser),
"PublicPhotoCount": models.CountPublicPhotos(user.ID),
"InnerCircleMinimumPublicPhotos": config.InnerCircleMinimumPublicPhotos,
"Pager": pager,
"LikeMap": likeMap,
"CommentMap": commentMap,
"ViewStyle": viewStyle,
"ExplicitCount": explicitCount,
}
if err := tmpl.Execute(w, r, vars); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
})
}