diff --git a/pkg/controller/photo/private.go b/pkg/controller/photo/private.go index 7b75eeb..31696f7 100644 --- a/pkg/controller/photo/private.go +++ b/pkg/controller/photo/private.go @@ -144,7 +144,7 @@ func Share() http.HandlerFunc { Type: models.NotificationPrivatePhoto, TableName: "__private_photos", TableID: currentUser.ID, - Link: fmt.Sprintf("/photo/u/%s", currentUser.Username), + Link: fmt.Sprintf("/photo/u/%s?visibility=private", currentUser.Username), } if err := models.CreateNotification(notif); err != nil { log.Error("Couldn't create PrivatePhoto notification: %s", err) diff --git a/pkg/controller/photo/site_gallery.go b/pkg/controller/photo/site_gallery.go index a369bfe..a10b875 100644 --- a/pkg/controller/photo/site_gallery.go +++ b/pkg/controller/photo/site_gallery.go @@ -40,7 +40,7 @@ func SiteGallery() http.HandlerFunc { } } if !sortOK { - sort = "created_at desc" + sort = sortWhitelist[0] } // Defaults. diff --git a/pkg/controller/photo/user_gallery.go b/pkg/controller/photo/user_gallery.go index 386609d..7477d1e 100644 --- a/pkg/controller/photo/user_gallery.go +++ b/pkg/controller/photo/user_gallery.go @@ -16,11 +16,37 @@ 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") + + // Whitelist for ordering options. + var sortWhitelist = []string{ + "created_at desc", + "created_at asc", + } + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // Query params. var ( viewStyle = r.FormValue("view") // cards (default), full + + // Search filters. + filterExplicit = r.FormValue("explicit") + filterVisibility = r.FormValue("visibility") + sort = r.FormValue("sort") + sortOK bool ) + + // Sort options. + for _, v := range sortWhitelist { + if sort == v { + sortOK = true + break + } + } + if !sortOK { + sort = sortWhitelist[0] + } + + // Defaults. if viewStyle != "full" { viewStyle = "cards" } @@ -45,9 +71,11 @@ func UserPhotos() http.HandlerFunc { session.FlashError(w, r, "Unexpected error: couldn't get CurrentUser") } var ( + areFriends = models.AreFriends(user.ID, currentUser.ID) + isPrivate = user.Visibility == models.UserVisibilityPrivate && !areFriends isOwnPhotos = currentUser.ID == user.ID isShy = currentUser.IsShy() - isShyFrom = !isOwnPhotos && (currentUser.IsShyFrom(user) || (isShy && !models.AreFriends(currentUser.ID, user.ID))) + isShyFrom = !isOwnPhotos && (currentUser.IsShyFrom(user) || (isShy && !areFriends)) ) // Bail early if we are shy from this user. @@ -77,10 +105,6 @@ func UserPhotos() http.HandlerFunc { } // 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) @@ -98,7 +122,7 @@ func UserPhotos() http.HandlerFunc { if isOwnPhotos || isGrantee || currentUser.HasAdminScope(config.ScopePhotoModerator) { visibility = append(visibility, models.PhotoPrivate) } - if isOwnPhotos || models.AreFriends(user.ID, currentUser.ID) || currentUser.HasAdminScope(config.ScopePhotoModerator) { + if isOwnPhotos || areFriends || currentUser.HasAdminScope(config.ScopePhotoModerator) { visibility = append(visibility, models.PhotoFriends) } @@ -107,27 +131,52 @@ func UserPhotos() http.HandlerFunc { visibility = append(visibility, models.PhotoInnerCircle) } - // Explicit photo filter? - explicit := currentUser.Explicit - if isOwnPhotos { - explicit = true + // If we are Filtering by Visibility, ensure the target visibility is accessible to us. + if filterVisibility != "" { + var isOK bool + for _, allowed := range visibility { + if allowed == models.PhotoVisibility(filterVisibility) { + isOK = true + break + } + } + + // If the filter is within the set we are allowed to see, update the set. + if isOK { + visibility = []models.PhotoVisibility{models.PhotoVisibility(filterVisibility)} + } else { + session.FlashError(w, r, "Could not filter pictures by that visibility setting: it is not available for you.") + visibility = []models.PhotoVisibility{models.PhotoNotAvailable} + } + } + + // Explicit photo filter? The default ("") will defer to the user's Explicit opt-in. + if filterExplicit == "" { + // If the viewer does not opt-in to explicit AND is not looking at their own gallery, + // then default the explicit filter to "do not show explicit" + if !currentUser.Explicit && !isOwnPhotos { + filterExplicit = "false" + } } // Get the page of photos. pager := &models.Pagination{ Page: 1, PerPage: config.PageSizeUserGallery, - Sort: "created_at desc", + Sort: sort, } pager.ParsePage(r) - photos, err := models.PaginateUserPhotos(user.ID, visibility, explicit, pager) + photos, err := models.PaginateUserPhotos(user.ID, models.UserGallery{ + Explicit: filterExplicit, + Visibility: visibility, + }, 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 { + if filterExplicit == "false" { explicitCount, _ = models.CountExplicitPhotos(user.ID, visibility) } @@ -145,6 +194,7 @@ func UserPhotos() http.HandlerFunc { "IsShyFrom": isShyFrom, "IsMyPrivateUnlockedFor": isGranted, // have WE granted THIS USER to see our private pics? "AreWeGrantedPrivate": isGrantee, // have THEY granted US private photo access. + "AreFriends": areFriends, "User": user, "Photos": photos, "PhotoCount": models.CountPhotosICanSee(user, currentUser), @@ -157,6 +207,11 @@ func UserPhotos() http.HandlerFunc { "CommentMap": commentMap, "ViewStyle": viewStyle, "ExplicitCount": explicitCount, + + // Search filters + "Sort": sort, + "FilterExplicit": filterExplicit, + "FilterVisibility": filterVisibility, } if err := tmpl.Execute(w, r, vars); err != nil { diff --git a/pkg/models/deletion/delete_user.go b/pkg/models/deletion/delete_user.go index 1d2747b..d0b36e5 100644 --- a/pkg/models/deletion/delete_user.go +++ b/pkg/models/deletion/delete_user.go @@ -61,8 +61,9 @@ func DeleteUserPhotos(userID uint64) error { for { photos, err := models.PaginateUserPhotos( userID, - models.PhotoVisibilityAll, - true, + models.UserGallery{ + Visibility: models.PhotoVisibilityAll, + }, pager, ) diff --git a/pkg/models/photo.go b/pkg/models/photo.go index d86eb6e..ce5f97f 100644 --- a/pkg/models/photo.go +++ b/pkg/models/photo.go @@ -34,6 +34,10 @@ const ( PhotoFriends PhotoVisibility = "friends" // only friends can see it PhotoPrivate PhotoVisibility = "private" // private PhotoInnerCircle PhotoVisibility = "circle" // inner circle + + // Special visibility in case, on User Gallery view, user applies a filter + // for friends-only picture but they are not friends with the user. + PhotoNotAvailable PhotoVisibility = "not_available" ) // PhotoVisibility preset settings. @@ -103,22 +107,41 @@ func GetPhotos(IDs []uint64) (map[uint64]*Photo, error) { return mp, result.Error } +// UserGallery configuration for filtering gallery pages. +type UserGallery struct { + Explicit string // "", "true", "false" + Visibility []PhotoVisibility +} + /* PaginateUserPhotos gets a page of photos belonging to a user ID. */ -func PaginateUserPhotos(userID uint64, visibility []PhotoVisibility, explicitOK bool, pager *Pagination) ([]*Photo, error) { - var p = []*Photo{} +func PaginateUserPhotos(userID uint64, conf UserGallery, pager *Pagination) ([]*Photo, error) { + var ( + p = []*Photo{} + wheres = []string{} + placeholders = []interface{}{} + ) - var explicit = []bool{false} - if explicitOK { - explicit = []bool{true, false} + var explicit = []bool{} + switch conf.Explicit { + case "true": + explicit = []bool{true} + case "false": + explicit = []bool{false} + } + + wheres = append(wheres, "user_id = ? AND visibility IN ?") + placeholders = append(placeholders, userID, conf.Visibility) + + if len(explicit) > 0 { + wheres = append(wheres, "explicit = ?") + placeholders = append(placeholders, explicit[0]) } query := DB.Where( - "user_id = ? AND visibility IN ? AND explicit IN ?", - userID, - visibility, - explicit, + strings.Join(wheres, " AND "), + placeholders..., ).Order( pager.Sort, ) diff --git a/web/templates/photo/gallery.html b/web/templates/photo/gallery.html index 9d2ab01..6fea9dd 100644 --- a/web/templates/photo/gallery.html +++ b/web/templates/photo/gallery.html @@ -198,6 +198,11 @@ {{.ExplicitCount}} explicit photo{{Pluralize64 .ExplicitCount}} hidden per your settings. {{end}} + {{else if .ExplicitCount}} + + + {{.ExplicitCount}} explicit photo{{Pluralize64 .ExplicitCount}} hidden per your settings. + {{end}} @@ -218,9 +223,9 @@ - {{if .IsSiteGallery}} +
-
+