diff --git a/frontend/src/component/p-photo-cards.vue b/frontend/src/component/p-photo-cards.vue index 393006d1c..3620b9813 100644 --- a/frontend/src/component/p-photo-cards.vue +++ b/frontend/src/component/p-photo-cards.vue @@ -81,8 +81,12 @@ - favorite - favorite_border + + favorite + + + favorite_border + @@ -117,7 +121,8 @@ - + {{ photo.Title | truncate(80) }} @@ -127,7 +132,8 @@ - + date_range {{ photo.getDateString() }} @@ -138,7 +144,8 @@ movie {{ photo.getVideoInfo() }} - + photo_camera {{ photo.getPhotoInfo() }} @@ -153,7 +160,8 @@ - + location_on {{ photo.locationInfo() }} @@ -161,6 +169,18 @@ + + + + + + Approve + + + + + diff --git a/frontend/src/model/photo.js b/frontend/src/model/photo.js index b673f48ee..e781b45d5 100644 --- a/frontend/src/model/photo.js +++ b/frontend/src/model/photo.js @@ -412,6 +412,10 @@ export class Photo extends RestModel { return "Unknown"; } + approve() { + return Api.post(this.getEntityResource() + "/approve"); + } + toggleLike() { this.Favorite = !this.Favorite; diff --git a/internal/api/photo.go b/internal/api/photo.go index 72f3551b3..df166a2ff 100644 --- a/internal/api/photo.go +++ b/internal/api/photo.go @@ -179,6 +179,39 @@ func GetPhotoYaml(router *gin.RouterGroup, conf *config.Config) { }) } +// POST /api/v1/photos/:uid/approve +// +// Parameters: +// uid: string PhotoUID as returned by the API +func ApprovePhoto(router *gin.RouterGroup, conf *config.Config) { + router.POST("/photos/:uid/approve", func(c *gin.Context) { + if Unauthorized(c, conf) { + c.AbortWithStatusJSON(http.StatusUnauthorized, ErrUnauthorized) + return + } + + id := c.Param("uid") + m, err := query.PhotoByUID(id) + + if err != nil { + c.AbortWithStatusJSON(http.StatusNotFound, ErrPhotoNotFound) + return + } + + if err := m.Approve(); err != nil { + log.Errorf("photo: %s", err.Error()) + c.AbortWithStatusJSON(http.StatusInternalServerError, ErrSaveFailed) + return + } + + SavePhotoAsYaml(m, conf) + + PublishPhotoEvent(EntityUpdated, id, c) + + c.JSON(http.StatusOK, gin.H{"photo": m}) + }) +} + // POST /api/v1/photos/:uid/like // // Parameters: diff --git a/internal/entity/photo.go b/internal/entity/photo.go index 8823744bd..98654a151 100644 --- a/internal/entity/photo.go +++ b/internal/entity/photo.go @@ -843,3 +843,29 @@ func (m *Photo) SetFavorite(favorite bool) error { return nil } + +// Approve approves a photo in review. +func (m *Photo) Approve() error { + if m.PhotoQuality >= 3 { + // Nothing to do. + return nil + } + + edited := time.Now().UTC() + m.EditedAt = &edited + m.PhotoQuality = m.QualityScore() + + if err := Db().Model(m).Updates(Photo{EditedAt: m.EditedAt, PhotoQuality: m.PhotoQuality}).Error; err != nil { + return err + } + + if err := UpdatePhotoCounts(); err != nil { + log.Errorf("photo: %s", err) + } + + event.Publish("count.review", event.Data{ + "count": -1, + }) + + return nil +} diff --git a/internal/server/routes.go b/internal/server/routes.go index 2956ac13f..8e09c98d0 100644 --- a/internal/server/routes.go +++ b/internal/server/routes.go @@ -38,6 +38,7 @@ func registerRoutes(router *gin.Engine, conf *config.Config) { api.GetPhotos(v1, conf) api.GetPhotoDownload(v1, conf) api.LinkPhoto(v1, conf) + api.ApprovePhoto(v1, conf) api.LikePhoto(v1, conf) api.DislikePhoto(v1, conf) api.AddPhotoLabel(v1, conf)