package photo import ( "fmt" "net/http" "path/filepath" "strconv" "strings" "code.nonshy.com/nonshy/website/pkg/chat" "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 } // In case an admin is editing this photo: remember the HTTP request current user, // before the currentUser may be set to the photo's owner below. var requestUser = currentUser // Do we have permission for this photo? if photo.UserID != currentUser.ID { if !currentUser.HasAdminScope(config.ScopePhotoModerator) { 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 { // Record if this change is going to make them a Shy Account. var wasShy = currentUser.IsShy() var ( caption = strings.TrimSpace(r.FormValue("caption")) altText = strings.TrimSpace(r.FormValue("alt_text")) isExplicit = r.FormValue("explicit") == "true" isGallery = r.FormValue("gallery") == "true" isPinned = r.FormValue("pinned") == "true" visibility = models.PhotoVisibility(r.FormValue("visibility")) // Profile pic fields setProfilePic = r.FormValue("intent") == "profile-pic" crop = pphoto.ParseCropCoords(r.FormValue("crop")) // Are we GOING private? goingPrivate = visibility == models.PhotoPrivate && visibility != photo.Visibility // Is the user fighting an 'Explicit' tag added by the community? isFightingExplicitFlag = photo.Flagged && photo.Explicit && !isExplicit ) if len(altText) > config.AltTextMaxLength { altText = altText[:config.AltTextMaxLength] } // Respect the Site Gallery throttle in case the user is messing around. if SiteGalleryThrottled { isGallery = false } // Diff for the ChangeLog. diffs := []models.FieldDiff{ models.NewFieldDiff("Caption", photo.Caption, caption), models.NewFieldDiff("Explicit", photo.Explicit, isExplicit), models.NewFieldDiff("Gallery", photo.Gallery, isGallery), models.NewFieldDiff("Pinned", photo.Pinned, isPinned), models.NewFieldDiff("Visibility", photo.Visibility, visibility), } // Admin label options. if requestUser.HasAdminScope(config.ScopePhotoModerator) { var adminLabel string if labels, ok := r.PostForm["admin_label"]; ok { adminLabel = strings.Join(labels, ",") } diffs = append(diffs, models.NewFieldDiff("Admin Label", photo.AdminLabel, adminLabel), ) photo.AdminLabel = adminLabel } // Admin label: forced explicit? if photo.HasAdminLabelForceExplicit() { isExplicit = true } photo.Caption = caption photo.AltText = altText photo.Explicit = isExplicit photo.Gallery = isGallery photo.Pinned = isPinned 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 } // If the user is fighting a recent Explicit flag from the community. if isFightingExplicitFlag { // Notify the admin (unless we are an admin). if !requestUser.IsAdmin { fb := &models.Feedback{ Intent: "report", Subject: "Explicit photo flag dispute", UserID: currentUser.ID, TableName: "photos", TableID: photo.ID, Message: "A user's photo was recently **flagged by the community** as Explicit, and its owner " + "has **removed** the Explicit setting.\n\n" + "Please check out the photo below and verify what its Explicit setting should be:", } if err := models.CreateFeedback(fb); err != nil { log.Error("Couldn't save feedback from user updating their DOB: %s", err) } } // Allow this change but clear the Flagged status. photo.Flagged = false // Clear the notification about this. models.RemoveSpecificNotification(currentUser.ID, models.NotificationExplicitPhoto, "photos", photo.ID) } 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!") // Log the change. models.LogUpdated(currentUser, requestUser, "photos", photo.ID, "Updated the photo's settings.", diffs) // Maybe kick them from the chat if this photo save makes them a Shy Account. currentUser.FlushCaches() if !wasShy && currentUser.IsShy() { if _, err := chat.MaybeDisconnectUser(currentUser); err != nil { log.Error("chat.MaybeDisconnectUser(%s#%d): %s", currentUser.Username, currentUser.ID, err) } } // If this picture has moved to Private, revoke any notification we gave about it before. if goingPrivate { 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, "/u/"+currentUser.Username+"/photos") return } var vars = map[string]interface{}{ "EditPhoto": photo, "SiteGalleryThrottled": SiteGalleryThrottled, "SiteGalleryThrottleLimit": config.SiteGalleryRateLimitMax, // Available admin labels enum. "RequestUser": requestUser, "AvailableAdminLabels": config.AdminLabelPhotoOptions, } 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. // // DEPRECATED: send them to the batch-edit endpoint. func Delete() http.HandlerFunc { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { templates.Redirect(w, fmt.Sprintf("/photo/batch-edit?intent=delete&id=%s", r.FormValue("id"))) }) }