236 lines
6.2 KiB
Go
236 lines
6.2 KiB
Go
package photo
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
"strconv"
|
|
|
|
"code.nonshy.com/nonshy/website/pkg/chat"
|
|
"code.nonshy.com/nonshy/website/pkg/log"
|
|
"code.nonshy.com/nonshy/website/pkg/models"
|
|
pphoto "code.nonshy.com/nonshy/website/pkg/photo"
|
|
"code.nonshy.com/nonshy/website/pkg/session"
|
|
"code.nonshy.com/nonshy/website/pkg/templates"
|
|
)
|
|
|
|
// BatchEdit controller (/photo/batch-edit?id=N) to change properties about your picture.
|
|
func BatchEdit() http.HandlerFunc {
|
|
tmpl := templates.Must("photo/batch_edit.html")
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
var (
|
|
// Form params
|
|
intent = r.FormValue("intent")
|
|
photoIDs []uint64
|
|
)
|
|
|
|
// Collect the photo ID params.
|
|
if value, ok := r.Form["id"]; ok {
|
|
for _, idStr := range value {
|
|
if photoID, err := strconv.Atoi(idStr); err == nil {
|
|
photoIDs = append(photoIDs, uint64(photoID))
|
|
} else {
|
|
log.Error("parsing photo ID %s: %s", idStr, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Validation.
|
|
if len(photoIDs) == 0 || len(photoIDs) > 100 {
|
|
session.FlashError(w, r, "Invalid number of photo IDs.")
|
|
templates.Redirect(w, "/")
|
|
return
|
|
}
|
|
|
|
// Find these photos by ID.
|
|
photos, err := models.GetPhotos(photoIDs)
|
|
if err != nil {
|
|
templates.NotFoundPage(w, r)
|
|
return
|
|
}
|
|
|
|
// Load the current user.
|
|
currentUser, err := session.CurrentUser(r)
|
|
if err != nil {
|
|
session.FlashError(w, r, "Unexpected error: couldn't get CurrentUser")
|
|
templates.Redirect(w, "/")
|
|
return
|
|
}
|
|
|
|
// Validate permission to edit all of these photos.
|
|
var (
|
|
ownerIDs []uint64
|
|
)
|
|
for _, photo := range photos {
|
|
|
|
if !photo.CanBeEditedBy(currentUser) {
|
|
templates.ForbiddenPage(w, r)
|
|
return
|
|
}
|
|
|
|
ownerIDs = append(ownerIDs, photo.UserID)
|
|
}
|
|
|
|
// Load the photo owners.
|
|
var (
|
|
owners, _ = models.MapUsers(currentUser, ownerIDs)
|
|
wasShy = map[uint64]bool{} // record if this change may make them shy
|
|
redirectURI = "/" // go first owner's gallery
|
|
|
|
// Are any of them a user's profile photo? (map userID->true) so we know
|
|
// who to unlink the picture from first and avoid a postgres error.
|
|
wasUserProfilePicture = map[uint64]bool{}
|
|
)
|
|
for _, user := range owners {
|
|
redirectURI = fmt.Sprintf("/u/%s/photos", user.Username)
|
|
wasShy[user.ID] = user.IsShy()
|
|
|
|
// Check if this user's profile ID is being deleted.
|
|
if user.ProfilePhotoID != nil {
|
|
if _, ok := photos[*user.ProfilePhotoID]; ok {
|
|
wasUserProfilePicture[user.ID] = true
|
|
}
|
|
}
|
|
}
|
|
|
|
// Confirm batch deletion or edit.
|
|
if r.Method == http.MethodPost {
|
|
|
|
confirm := r.PostFormValue("confirm") == "true"
|
|
if !confirm {
|
|
session.FlashError(w, r, "Confirm you want to modify this photo.")
|
|
templates.Redirect(w, redirectURI)
|
|
return
|
|
}
|
|
|
|
// Which intent are they executing on?
|
|
switch intent {
|
|
case "delete":
|
|
batchDeletePhotos(w, r, currentUser, photos, wasUserProfilePicture, owners, redirectURI)
|
|
case "visibility":
|
|
batchUpdateVisibility(w, r, currentUser, photos, owners)
|
|
default:
|
|
session.FlashError(w, r, "Unknown intent")
|
|
}
|
|
|
|
// Maybe kick them from chat if this deletion makes them into a Shy Account.
|
|
for _, user := range owners {
|
|
user.FlushCaches()
|
|
if !wasShy[user.ID] && user.IsShy() {
|
|
if _, err := chat.MaybeDisconnectUser(user); err != nil {
|
|
log.Error("chat.MaybeDisconnectUser(%s#%d): %s", user.Username, user.ID, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Return the user to their gallery.
|
|
templates.Redirect(w, redirectURI)
|
|
return
|
|
}
|
|
|
|
var vars = map[string]interface{}{
|
|
"Intent": intent,
|
|
"Photos": photos,
|
|
}
|
|
|
|
if err := tmpl.Execute(w, r, vars); err != nil {
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
})
|
|
}
|
|
|
|
// Batch DELETE executive handler.
|
|
func batchDeletePhotos(
|
|
w http.ResponseWriter,
|
|
r *http.Request,
|
|
currentUser *models.User,
|
|
photos map[uint64]*models.Photo,
|
|
wasUserProfilePicture map[uint64]bool,
|
|
owners map[uint64]*models.User,
|
|
redirectURI string,
|
|
) {
|
|
// Delete all the photos.
|
|
for _, photo := range photos {
|
|
|
|
// Was this someone's profile picture ID?
|
|
if wasUserProfilePicture[photo.UserID] {
|
|
log.Debug("Delete Photo: was the user's profile photo, unset ProfilePhotoID")
|
|
if owner, ok := owners[photo.UserID]; ok {
|
|
if err := owner.RemoveProfilePhoto(); err != nil {
|
|
session.FlashError(w, r, "Error unsetting your current profile photo: %s", err)
|
|
templates.Redirect(w, redirectURI)
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
// Remove the images from disk.
|
|
for _, filename := range []string{
|
|
photo.Filename,
|
|
photo.CroppedFilename,
|
|
} {
|
|
if len(filename) > 0 {
|
|
if err := pphoto.Delete(filename); err != nil {
|
|
log.Error("Delete Photo: couldn't remove file from disk: %s: %s", filename, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Take back notifications on it.
|
|
models.RemoveNotification("photos", photo.ID)
|
|
|
|
if err := photo.Delete(); err != nil {
|
|
session.FlashError(w, r, "Couldn't delete photo: %s", err)
|
|
templates.Redirect(w, redirectURI)
|
|
return
|
|
}
|
|
|
|
// Log the change.
|
|
if owner, ok := owners[photo.UserID]; ok {
|
|
models.LogDeleted(owner, currentUser, "photos", photo.ID, "Deleted the photo.", photo)
|
|
}
|
|
}
|
|
|
|
session.Flash(w, r, "%d photo(s) deleted!", len(photos))
|
|
}
|
|
|
|
// Batch DELETE executive handler.
|
|
func batchUpdateVisibility(
|
|
w http.ResponseWriter,
|
|
r *http.Request,
|
|
currentUser *models.User,
|
|
photos map[uint64]*models.Photo,
|
|
owners map[uint64]*models.User,
|
|
) {
|
|
// Visibility setting.
|
|
visibility := r.PostFormValue("visibility")
|
|
|
|
// Delete all the photos.
|
|
for _, photo := range photos {
|
|
|
|
// Diff for the ChangeLog.
|
|
diffs := []models.FieldDiff{
|
|
models.NewFieldDiff("Visibility", photo.Visibility, visibility),
|
|
}
|
|
|
|
photo.Visibility = models.PhotoVisibility(visibility)
|
|
|
|
// If going private, take back notifications on it.
|
|
if photo.Visibility == models.PhotoPrivate {
|
|
models.RemoveNotification("photos", photo.ID)
|
|
}
|
|
|
|
if err := photo.Save(); err != nil {
|
|
session.FlashError(w, r, "Error saving photo #%d: %s", photo.ID, err)
|
|
}
|
|
|
|
// Log the change.
|
|
if owner, ok := owners[photo.UserID]; ok {
|
|
// Log the change.
|
|
models.LogUpdated(owner, currentUser, "photos", photo.ID, "Updated the photo's settings.", diffs)
|
|
}
|
|
}
|
|
|
|
session.Flash(w, r, "%d photo(s) updated!", len(photos))
|
|
}
|