diff --git a/pkg/controller/account/profile.go b/pkg/controller/account/profile.go
index 7b9da46..41f59d2 100644
--- a/pkg/controller/account/profile.go
+++ b/pkg/controller/account/profile.go
@@ -72,9 +72,9 @@ func Profile() http.HandlerFunc {
// Inject relationship booleans for profile picture display.
models.SetUserRelationships(currentUser, []*models.User{user})
- // Admin user can always see the profile pic - but only on this page. Other avatar displays
- // will show the yellow or pink shy.png if the admin is not friends or not granted.
- if currentUser.IsAdmin {
+ // Admin user (photo moderator) can always see the profile pic - but only on this page.
+ // Other avatar displays will show the yellow or pink shy.png if the admin is not friends or not granted.
+ if currentUser.HasAdminScope(config.ScopePhotoModerator) {
user.UserRelationship.IsFriend = true
user.UserRelationship.IsPrivateGranted = true
}
diff --git a/pkg/controller/api/likes.go b/pkg/controller/api/likes.go
index 53df2ac..4243909 100644
--- a/pkg/controller/api/likes.go
+++ b/pkg/controller/api/likes.go
@@ -98,20 +98,7 @@ func Likes() http.HandlerFunc {
if user, err := models.GetUser(photo.UserID); err == nil {
// Safety check: if the current user should not see this picture, they can not "Like" it.
// Example: you unfriended them but they still had the image on their old browser page.
- var unallowed bool
- if currentUser.ID != user.ID {
- if (photo.Visibility == models.PhotoFriends && !models.AreFriends(user.ID, currentUser.ID)) ||
- (photo.Visibility == models.PhotoPrivate && !models.IsPrivateUnlocked(user.ID, currentUser.ID)) {
- unallowed = true
- }
- }
-
- // Blocking safety check: if either user blocks the other, liking is not allowed.
- if models.IsBlocking(currentUser.ID, user.ID) {
- unallowed = true
- }
-
- if unallowed {
+ if ok, _ := photo.ShouldBeSeenBy(currentUser); !ok {
SendJSON(w, http.StatusForbidden, Response{
Error: "You are not allowed to like that photo.",
})
@@ -121,7 +108,7 @@ func Likes() http.HandlerFunc {
// Mark this photo as 'viewed' if it received a like.
// Example: on a gallery view the photo is only 'viewed' if interacted with (lightbox),
// going straight for the 'Like' button should count as well.
- photo.View(currentUser.ID)
+ photo.View(currentUser)
targetUser = user
}
diff --git a/pkg/controller/api/photo.go b/pkg/controller/api/photo.go
index c4bd91e..27e8bcd 100644
--- a/pkg/controller/api/photo.go
+++ b/pkg/controller/api/photo.go
@@ -49,7 +49,7 @@ func ViewPhoto() http.HandlerFunc {
}
// Check permission to have seen this photo.
- if ok, err := photo.CanBeSeenBy(currentUser); !ok {
+ if ok, err := photo.ShouldBeSeenBy(currentUser); !ok {
log.Error("Photo %d can't be seen by %s: %s", photo.ID, currentUser.Username, err)
SendJSON(w, http.StatusNotFound, Response{
Error: "Photo Not Found",
@@ -58,7 +58,7 @@ func ViewPhoto() http.HandlerFunc {
}
// Mark a view.
- if err := photo.View(currentUser.ID); err != nil {
+ if err := photo.View(currentUser); err != nil {
log.Error("Update photo(%d) views: %s", photo.ID, err)
}
diff --git a/pkg/controller/photo/view.go b/pkg/controller/photo/view.go
index c34af78..4bbaca5 100644
--- a/pkg/controller/photo/view.go
+++ b/pkg/controller/photo/view.go
@@ -85,7 +85,7 @@ func View() http.HandlerFunc {
_, isSubscribed := models.IsSubscribed(currentUser, "photos", photo.ID)
// Mark this photo as "Viewed" by the user.
- if err := photo.View(currentUser.ID); err != nil {
+ if err := photo.View(currentUser); err != nil {
log.Error("Update photo(%d) views: %s", photo.ID, err)
}
diff --git a/pkg/models/photo.go b/pkg/models/photo.go
index 70715db..ddbc818 100644
--- a/pkg/models/photo.go
+++ b/pkg/models/photo.go
@@ -109,8 +109,27 @@ func GetPhotos(IDs []uint64) (map[uint64]*Photo, error) {
// CanBeSeenBy checks whether a photo can be seen by the current user.
//
+// An admin user with omni photo view permission can always see the photo.
+//
// Note: this function incurs several DB queries to look up the photo's owner and block lists.
func (p *Photo) CanBeSeenBy(currentUser *User) (bool, error) {
+ // Admins with photo moderator ability can always see.
+ if currentUser.HasAdminScope(config.ScopePhotoModerator) {
+ return true, nil
+ }
+
+ return p.ShouldBeSeenBy(currentUser)
+}
+
+// ShouldBeSeenBy checks whether a photo should be seen by the current user.
+//
+// Even if the current user is an admin with photo moderator ability, this function will return
+// whether the admin 'should' be able to see if not for their admin status. Example: a private
+// photo may be shown to the admin so they can moderate it, but they shouldn't be able to "like"
+// it or mark it as "viewed."
+//
+// Note: this function incurs several DB queries to look up the photo's owner and block lists.
+func (p *Photo) ShouldBeSeenBy(currentUser *User) (bool, error) {
// Find the photo's owner.
user, err := GetUser(p.UserID)
if err != nil {
@@ -120,7 +139,7 @@ func (p *Photo) CanBeSeenBy(currentUser *User) (bool, error) {
var isOwnPhoto = currentUser.ID == user.ID
// Is either one blocking?
- if !currentUser.IsAdmin && IsBlocking(currentUser.ID, user.ID) {
+ if IsBlocking(currentUser.ID, user.ID) {
return false, errors.New("is blocking")
}
@@ -129,13 +148,13 @@ func (p *Photo) CanBeSeenBy(currentUser *User) (bool, error) {
areFriends = AreFriends(user.ID, currentUser.ID)
isPrivate = user.Visibility == UserVisibilityPrivate && !areFriends
)
- if isPrivate && !currentUser.IsAdmin && !isOwnPhoto {
+ if isPrivate && !isOwnPhoto {
return false, errors.New("user is private and we aren't friends")
}
// Is this a private photo and are we allowed to see?
isGranted := IsPrivateUnlocked(user.ID, currentUser.ID)
- if p.Visibility == PhotoPrivate && !isGranted && !isOwnPhoto && !currentUser.IsAdmin {
+ if p.Visibility == PhotoPrivate && !isGranted && !isOwnPhoto {
return false, errors.New("photo is private")
}
@@ -193,14 +212,19 @@ func PaginateUserPhotos(userID uint64, conf UserGallery, pager *Pagination) ([]*
// View a photo, incrementing its Views count but not its UpdatedAt.
// Debounced with a Redis key.
-func (p *Photo) View(userID uint64) error {
+func (p *Photo) View(user *User) error {
// The owner of the photo does not count views.
- if p.UserID == userID {
+ if p.UserID == user.ID {
return nil
}
+ // Should the viewer be able to see this, regardless of their admin ability?
+ if ok, err := p.ShouldBeSeenBy(user); !ok {
+ return err
+ }
+
// Debounce this.
- var redisKey = fmt.Sprintf(config.PhotoViewDebounceRedisKey, userID, p.ID)
+ var redisKey = fmt.Sprintf(config.PhotoViewDebounceRedisKey, user.ID, p.ID)
if redis.Exists(redisKey) {
return nil
}
diff --git a/pkg/models/user_relationship.go b/pkg/models/user_relationship.go
index 1c0bca0..911777c 100644
--- a/pkg/models/user_relationship.go
+++ b/pkg/models/user_relationship.go
@@ -42,6 +42,8 @@ func SetUserRelationships(currentUser *User, users []*User) error {
// Inject the UserRelationships.
for _, u := range users {
+ u.UserRelationship.Computed = true
+
if u.ID == currentUser.ID {
// Current user - set both bools to true - you can always see your own profile pic.
u.UserRelationship.IsFriend = true
diff --git a/web/templates/account/profile.html b/web/templates/account/profile.html
index 75fd6b1..4b2ffed 100644
--- a/web/templates/account/profile.html
+++ b/web/templates/account/profile.html
@@ -11,7 +11,9 @@
{{if or (not .CurrentUser) .IsExternalView}}
{{else}}
-
+
+
+
{{end}}
diff --git a/web/templates/photo/gallery.html b/web/templates/photo/gallery.html
index 6fbfa90..004701f 100644
--- a/web/templates/photo/gallery.html
+++ b/web/templates/photo/gallery.html
@@ -804,7 +804,7 @@
}).then(response => response.json())
.then(data => {
if (data.StatusCode !== 200) {
- window.alert(data.data.error);
+ console.error("When marking photo %d as viewed: status code %d: %s", photoID, data.StatusCode, data.data.error);
return;
}
}).catch(window.alert);