package photo import ( "fmt" "net/http" "path/filepath" "strconv" "code.nonshy.com/nonshy/website/pkg/config" "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" ) // Edit controller (/photo/edit?id=N) to change properties about your picture. func Edit() http.HandlerFunc { // Reuse the upload page but with an EditPhoto variable. tmpl := templates.Must("photo/upload.html") return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // Query params. photoID, err := strconv.Atoi(r.FormValue("id")) if err != nil { session.FlashError(w, r, "Photo 'id' parameter required.") templates.Redirect(w, "/") return } // Find this photo by ID. photo, err := models.GetPhoto(uint64(photoID)) 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 } // Do we have permission for this photo? if photo.UserID != currentUser.ID { if !currentUser.IsAdmin { templates.ForbiddenPage(w, r) return } // Find the owner of this photo and assume currentUser is them for the remainder // of this controller. if user, err := models.GetUser(photo.UserID); err != nil { session.FlashError(w, r, "Couldn't get the owner User for this photo!") templates.Redirect(w, "/") return } else { currentUser = user } } // Is the user throttled for Site Gallery photo uploads? var SiteGalleryThrottled = models.IsSiteGalleryThrottled(currentUser, photo) // Are we saving the changes? if r.Method == http.MethodPost { var ( caption = r.FormValue("caption") isExplicit = r.FormValue("explicit") == "true" isGallery = r.FormValue("gallery") == "true" visibility = models.PhotoVisibility(r.FormValue("visibility")) // Profile pic fields setProfilePic = r.FormValue("intent") == "profile-pic" crop = pphoto.ParseCropCoords(r.FormValue("crop")) // Re-compute the face score (admin only) recomputeFaceScore = r.FormValue("recompute_face_score") == "true" && currentUser.IsAdmin // Are we GOING private or changing to Inner Circle? goingPrivate = visibility == models.PhotoPrivate && visibility != photo.Visibility goingCircle = visibility == models.PhotoInnerCircle && visibility != photo.Visibility ) // Respect the Site Gallery throttle in case the user is messing around. if SiteGalleryThrottled { isGallery = false } photo.Caption = caption photo.Explicit = isExplicit photo.Gallery = isGallery photo.Visibility = visibility // Can not use a GIF as profile pic. if setProfilePic && filepath.Ext(photo.Filename) == ".gif" { session.FlashError(w, r, "You can not use a GIF as your profile picture.") templates.Redirect(w, "/") return } // Are we cropping ourselves a new profile pic? log.Error("Profile pic? %+v and crop is: %+v", setProfilePic, crop) if setProfilePic && crop != nil && len(crop) >= 4 { cropFilename, err := pphoto.ReCrop(photo.Filename, crop[0], crop[1], crop[2], crop[3]) log.Error("ReCrop got: %s, %s", cropFilename, err) if err != nil { session.FlashError(w, r, "Couldn't re-crop for profile picture: %s", err) } else { // If there was an old profile pic, remove it from disk. if photo.CroppedFilename != "" { pphoto.Delete(photo.CroppedFilename) } photo.CroppedFilename = cropFilename log.Warn("HERE WE SET (%s) ON PHOTO (%+v)", cropFilename, photo) } } else { setProfilePic = false } log.Error("SAVING PHOTO: %+v", photo) // Are we re-computing the face score? if recomputeFaceScore { score, err := pphoto.ComputeFaceScore(pphoto.DiskPath(photo.Filename)) if err != nil { session.FlashError(w, r, "Face score: %s", err) } else { session.Flash(w, r, "Face score recomputed!") photo.FaceScore = &score } } if err := photo.Save(); err != nil { session.FlashError(w, r, "Couldn't save photo: %s", err) } // Set their profile pic to this one. if setProfilePic { currentUser.ProfilePhoto = *photo log.Error("Set user ProfilePhotoID=%d", photo.ID) if err := currentUser.Save(); err != nil { session.FlashError(w, r, "Couldn't save user: %s", err) } } // Flash success. session.Flash(w, r, "Photo settings updated!") // If this picture has moved to Private, revoke any notification we gave about it before. if goingPrivate || goingCircle { log.Info("The picture is GOING PRIVATE (to %s), revoke any notifications about it", photo.Visibility) models.RemoveNotification("photos", photo.ID) } // Return the user to their gallery. templates.Redirect(w, "/photo/u/"+currentUser.Username) return } var vars = map[string]interface{}{ "EditPhoto": photo, "SiteGalleryThrottled": SiteGalleryThrottled, "SiteGalleryThrottleLimit": config.SiteGalleryRateLimitMax, } if err := tmpl.Execute(w, r, vars); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } }) } // Delete controller (/photo/Delete?id=N) to change properties about your picture. func Delete() http.HandlerFunc { // Reuse the upload page but with an EditPhoto variable. tmpl := templates.Must("photo/delete.html") return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // Query params. photoID, err := strconv.Atoi(r.FormValue("id")) if err != nil { log.Error("photo.Delete: failed to parse `id` param (%s) as int: %s", r.FormValue("id"), err) session.FlashError(w, r, "Photo 'id' parameter required.") templates.Redirect(w, "/") return } // Page to redirect to in case of errors. redirect := fmt.Sprintf("%s?id=%d", r.URL.Path, photoID) // Find this photo by ID. photo, err := models.GetPhoto(uint64(photoID)) 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 } // Do we have permission for this photo? if photo.UserID != currentUser.ID { if !currentUser.IsAdmin { templates.ForbiddenPage(w, r) return } // Find the owner of this photo and assume currentUser is them for the remainder // of this controller. if user, err := models.GetUser(photo.UserID); err != nil { session.FlashError(w, r, "Couldn't get the owner User for this photo!") templates.Redirect(w, "/") return } else { currentUser = user } } // Confirm deletion? if r.Method == http.MethodPost { confirm := r.PostFormValue("confirm") == "true" if !confirm { session.FlashError(w, r, "Confirm you want to delete this photo.") templates.Redirect(w, redirect) return } // Was this our profile picture? if currentUser.ProfilePhotoID != nil && *currentUser.ProfilePhotoID == photo.ID { log.Debug("Delete Photo: was the user's profile photo, unset ProfilePhotoID") if err := currentUser.RemoveProfilePhoto(); err != nil { session.FlashError(w, r, "Error unsetting your current profile photo: %s", err) templates.Redirect(w, redirect) 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, redirect) return } session.Flash(w, r, "Photo deleted!") // Return the user to their gallery. templates.Redirect(w, "/photo/u/"+currentUser.Username) return } var vars = map[string]interface{}{ "Photo": photo, } if err := tmpl.Execute(w, r, vars); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } }) }