Tweak admin permissions and photo view counts
* Profile pictures on profile pages now link to the gallery when clicked. * Admins can no longer automatically see the default profile pic on profile pages unless they have photo moderator ability. * Photo view counts are not added when an admin with photo moderator ability should not have otherwise been able to see the photo.
This commit is contained in:
parent
3fdae1d8d7
commit
f2e847922f
|
@ -72,9 +72,9 @@ func Profile() http.HandlerFunc {
|
||||||
// Inject relationship booleans for profile picture display.
|
// Inject relationship booleans for profile picture display.
|
||||||
models.SetUserRelationships(currentUser, []*models.User{user})
|
models.SetUserRelationships(currentUser, []*models.User{user})
|
||||||
|
|
||||||
// Admin user can always see the profile pic - but only on this page. Other avatar displays
|
// Admin user (photo moderator) can always see the profile pic - but only on this page.
|
||||||
// will show the yellow or pink shy.png if the admin is not friends or not granted.
|
// Other avatar displays will show the yellow or pink shy.png if the admin is not friends or not granted.
|
||||||
if currentUser.IsAdmin {
|
if currentUser.HasAdminScope(config.ScopePhotoModerator) {
|
||||||
user.UserRelationship.IsFriend = true
|
user.UserRelationship.IsFriend = true
|
||||||
user.UserRelationship.IsPrivateGranted = true
|
user.UserRelationship.IsPrivateGranted = true
|
||||||
}
|
}
|
||||||
|
|
|
@ -98,20 +98,7 @@ func Likes() http.HandlerFunc {
|
||||||
if user, err := models.GetUser(photo.UserID); err == nil {
|
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.
|
// 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.
|
// Example: you unfriended them but they still had the image on their old browser page.
|
||||||
var unallowed bool
|
if ok, _ := photo.ShouldBeSeenBy(currentUser); !ok {
|
||||||
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 {
|
|
||||||
SendJSON(w, http.StatusForbidden, Response{
|
SendJSON(w, http.StatusForbidden, Response{
|
||||||
Error: "You are not allowed to like that photo.",
|
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.
|
// Mark this photo as 'viewed' if it received a like.
|
||||||
// Example: on a gallery view the photo is only 'viewed' if interacted with (lightbox),
|
// Example: on a gallery view the photo is only 'viewed' if interacted with (lightbox),
|
||||||
// going straight for the 'Like' button should count as well.
|
// going straight for the 'Like' button should count as well.
|
||||||
photo.View(currentUser.ID)
|
photo.View(currentUser)
|
||||||
|
|
||||||
targetUser = user
|
targetUser = user
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,7 +49,7 @@ func ViewPhoto() http.HandlerFunc {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check permission to have seen this photo.
|
// 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)
|
log.Error("Photo %d can't be seen by %s: %s", photo.ID, currentUser.Username, err)
|
||||||
SendJSON(w, http.StatusNotFound, Response{
|
SendJSON(w, http.StatusNotFound, Response{
|
||||||
Error: "Photo Not Found",
|
Error: "Photo Not Found",
|
||||||
|
@ -58,7 +58,7 @@ func ViewPhoto() http.HandlerFunc {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mark a view.
|
// 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)
|
log.Error("Update photo(%d) views: %s", photo.ID, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -85,7 +85,7 @@ func View() http.HandlerFunc {
|
||||||
_, isSubscribed := models.IsSubscribed(currentUser, "photos", photo.ID)
|
_, isSubscribed := models.IsSubscribed(currentUser, "photos", photo.ID)
|
||||||
|
|
||||||
// Mark this photo as "Viewed" by the user.
|
// 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)
|
log.Error("Update photo(%d) views: %s", photo.ID, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -109,8 +109,27 @@ func GetPhotos(IDs []uint64) (map[uint64]*Photo, error) {
|
||||||
|
|
||||||
// CanBeSeenBy checks whether a photo can be seen by the current user.
|
// 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.
|
// 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) {
|
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.
|
// Find the photo's owner.
|
||||||
user, err := GetUser(p.UserID)
|
user, err := GetUser(p.UserID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -120,7 +139,7 @@ func (p *Photo) CanBeSeenBy(currentUser *User) (bool, error) {
|
||||||
var isOwnPhoto = currentUser.ID == user.ID
|
var isOwnPhoto = currentUser.ID == user.ID
|
||||||
|
|
||||||
// Is either one blocking?
|
// Is either one blocking?
|
||||||
if !currentUser.IsAdmin && IsBlocking(currentUser.ID, user.ID) {
|
if IsBlocking(currentUser.ID, user.ID) {
|
||||||
return false, errors.New("is blocking")
|
return false, errors.New("is blocking")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -129,13 +148,13 @@ func (p *Photo) CanBeSeenBy(currentUser *User) (bool, error) {
|
||||||
areFriends = AreFriends(user.ID, currentUser.ID)
|
areFriends = AreFriends(user.ID, currentUser.ID)
|
||||||
isPrivate = user.Visibility == UserVisibilityPrivate && !areFriends
|
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")
|
return false, errors.New("user is private and we aren't friends")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Is this a private photo and are we allowed to see?
|
// Is this a private photo and are we allowed to see?
|
||||||
isGranted := IsPrivateUnlocked(user.ID, currentUser.ID)
|
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")
|
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.
|
// View a photo, incrementing its Views count but not its UpdatedAt.
|
||||||
// Debounced with a Redis key.
|
// 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.
|
// The owner of the photo does not count views.
|
||||||
if p.UserID == userID {
|
if p.UserID == user.ID {
|
||||||
return nil
|
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.
|
// 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) {
|
if redis.Exists(redisKey) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,6 +42,8 @@ func SetUserRelationships(currentUser *User, users []*User) error {
|
||||||
|
|
||||||
// Inject the UserRelationships.
|
// Inject the UserRelationships.
|
||||||
for _, u := range users {
|
for _, u := range users {
|
||||||
|
u.UserRelationship.Computed = true
|
||||||
|
|
||||||
if u.ID == currentUser.ID {
|
if u.ID == currentUser.ID {
|
||||||
// Current user - set both bools to true - you can always see your own profile pic.
|
// Current user - set both bools to true - you can always see your own profile pic.
|
||||||
u.UserRelationship.IsFriend = true
|
u.UserRelationship.IsFriend = true
|
||||||
|
|
|
@ -11,7 +11,9 @@
|
||||||
{{if or (not .CurrentUser) .IsExternalView}}
|
{{if or (not .CurrentUser) .IsExternalView}}
|
||||||
<img src="{{.User.VisibleAvatarURL nil}}" data-photo-id="{{.User.ProfilePhoto.ID}}">
|
<img src="{{.User.VisibleAvatarURL nil}}" data-photo-id="{{.User.ProfilePhoto.ID}}">
|
||||||
{{else}}
|
{{else}}
|
||||||
<img src="{{.User.VisibleAvatarURL .CurrentUser}}" data-photo-id="{{.User.ProfilePhoto.ID}}">
|
<a href="/u/{{.User.Username}}/photos">
|
||||||
|
<img src="{{.User.VisibleAvatarURL .CurrentUser}}" data-photo-id="{{.User.ProfilePhoto.ID}}">
|
||||||
|
</a>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
<!-- CurrentUser can upload a new profile pic -->
|
<!-- CurrentUser can upload a new profile pic -->
|
||||||
|
|
|
@ -804,7 +804,7 @@
|
||||||
}).then(response => response.json())
|
}).then(response => response.json())
|
||||||
.then(data => {
|
.then(data => {
|
||||||
if (data.StatusCode !== 200) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
}).catch(window.alert);
|
}).catch(window.alert);
|
||||||
|
|
Loading…
Reference in New Issue
Block a user