Albums: Improve parameter validation for database queries #3320
Signed-off-by: Michael Mayer <michael@photoprism.app>
This commit is contained in:
parent
9a3d61c99f
commit
b6378a5c1f
|
@ -82,7 +82,7 @@ func CreateAlbum(router *gin.RouterGroup) {
|
|||
albumMutex.Lock()
|
||||
defer albumMutex.Unlock()
|
||||
|
||||
a := entity.NewUserAlbum(f.AlbumTitle, entity.AlbumDefault, s.UserUID)
|
||||
a := entity.NewUserAlbum(f.AlbumTitle, entity.AlbumManual, s.UserUID)
|
||||
a.AlbumFavorite = f.AlbumFavorite
|
||||
|
||||
// Existing album?
|
||||
|
@ -108,9 +108,9 @@ func CreateAlbum(router *gin.RouterGroup) {
|
|||
}
|
||||
}
|
||||
|
||||
// Publish event and create/update YAML backup.
|
||||
UpdateClientConfig()
|
||||
// PublishAlbumEvent(EntityCreated, a.AlbumUID, c)
|
||||
|
||||
// Update album YAML backup.
|
||||
SaveAlbumAsYaml(*a)
|
||||
|
||||
// Return as JSON.
|
||||
|
@ -162,8 +162,7 @@ func UpdateAlbum(router *gin.RouterGroup) {
|
|||
|
||||
UpdateClientConfig()
|
||||
|
||||
// PublishAlbumEvent(EntityUpdated, uid, c)
|
||||
|
||||
// Update album YAML backup.
|
||||
SaveAlbumAsYaml(a)
|
||||
|
||||
c.JSON(http.StatusOK, a)
|
||||
|
@ -212,6 +211,7 @@ func DeleteAlbum(router *gin.RouterGroup) {
|
|||
|
||||
UpdateClientConfig()
|
||||
|
||||
// Update album YAML backup.
|
||||
SaveAlbumAsYaml(a)
|
||||
|
||||
c.JSON(http.StatusOK, a)
|
||||
|
@ -250,6 +250,7 @@ func LikeAlbum(router *gin.RouterGroup) {
|
|||
|
||||
PublishAlbumEvent(EntityUpdated, id, c)
|
||||
|
||||
// Update album YAML backup.
|
||||
SaveAlbumAsYaml(a)
|
||||
|
||||
c.JSON(http.StatusOK, i18n.NewResponse(http.StatusOK, i18n.MsgChangesSaved))
|
||||
|
@ -288,6 +289,7 @@ func DislikeAlbum(router *gin.RouterGroup) {
|
|||
|
||||
PublishAlbumEvent(EntityUpdated, id, c)
|
||||
|
||||
// Update album YAML backup.
|
||||
SaveAlbumAsYaml(a)
|
||||
|
||||
c.JSON(http.StatusOK, i18n.NewResponse(http.StatusOK, i18n.MsgChangesSaved))
|
||||
|
@ -344,6 +346,7 @@ func CloneAlbums(router *gin.RouterGroup) {
|
|||
|
||||
PublishAlbumEvent(EntityUpdated, a.AlbumUID, c)
|
||||
|
||||
// Update album YAML backup.
|
||||
SaveAlbumAsYaml(a)
|
||||
}
|
||||
|
||||
|
@ -375,6 +378,12 @@ func AddPhotosToAlbum(router *gin.RouterGroup) {
|
|||
if err != nil {
|
||||
AbortAlbumNotFound(c)
|
||||
return
|
||||
} else if !a.HasID() {
|
||||
AbortAlbumNotFound(c)
|
||||
return
|
||||
} else if f.Empty() {
|
||||
Abort(c, http.StatusBadRequest, i18n.ErrNoItemsSelected)
|
||||
return
|
||||
}
|
||||
|
||||
// Fetch selection from index.
|
||||
|
@ -399,6 +408,7 @@ func AddPhotosToAlbum(router *gin.RouterGroup) {
|
|||
|
||||
PublishAlbumEvent(EntityUpdated, a.AlbumUID, c)
|
||||
|
||||
// Update album YAML backup.
|
||||
SaveAlbumAsYaml(a)
|
||||
}
|
||||
|
||||
|
@ -434,6 +444,9 @@ func RemovePhotosFromAlbum(router *gin.RouterGroup) {
|
|||
if err != nil {
|
||||
AbortAlbumNotFound(c)
|
||||
return
|
||||
} else if !a.HasID() {
|
||||
AbortAlbumNotFound(c)
|
||||
return
|
||||
}
|
||||
|
||||
removed := a.RemovePhotos(f.Photos)
|
||||
|
@ -449,6 +462,7 @@ func RemovePhotosFromAlbum(router *gin.RouterGroup) {
|
|||
|
||||
PublishAlbumEvent(EntityUpdated, a.AlbumUID, c)
|
||||
|
||||
// Update album YAML backup.
|
||||
SaveAlbumAsYaml(a)
|
||||
}
|
||||
|
||||
|
|
|
@ -555,13 +555,13 @@ func (c *Config) ClientUser(withSettings bool) ClientConfig {
|
|||
Table("albums").
|
||||
Select("SUM(album_type = ?) AS albums, SUM(album_type = ?) AS moments, SUM(album_type = ?) AS months, SUM(album_type = ?) AS states, SUM(album_type = ?) AS folders, "+
|
||||
"SUM(album_type = ? AND album_private = 1) AS private_albums, SUM(album_type = ? AND album_private = 1) AS private_moments, SUM(album_type = ? AND album_private = 1) AS private_months, SUM(album_type = ? AND album_private = 1) AS private_states, SUM(album_type = ? AND album_private = 1) AS private_folders",
|
||||
entity.AlbumDefault, entity.AlbumMoment, entity.AlbumMonth, entity.AlbumState, entity.AlbumFolder, entity.AlbumDefault, entity.AlbumMoment, entity.AlbumMonth, entity.AlbumState, entity.AlbumFolder).
|
||||
entity.AlbumManual, entity.AlbumMoment, entity.AlbumMonth, entity.AlbumState, entity.AlbumFolder, entity.AlbumManual, entity.AlbumMoment, entity.AlbumMonth, entity.AlbumState, entity.AlbumFolder).
|
||||
Where("deleted_at IS NULL AND (albums.album_type <> 'folder' OR albums.album_path IN (SELECT photos.photo_path FROM photos WHERE photos.photo_private = 0 AND photos.deleted_at IS NULL))").
|
||||
Take(&cfg.Count)
|
||||
} else {
|
||||
c.Db().
|
||||
Table("albums").
|
||||
Select("SUM(album_type = ?) AS albums, SUM(album_type = ?) AS moments, SUM(album_type = ?) AS months, SUM(album_type = ?) AS states, SUM(album_type = ?) AS folders", entity.AlbumDefault, entity.AlbumMoment, entity.AlbumMonth, entity.AlbumState, entity.AlbumFolder).
|
||||
Select("SUM(album_type = ?) AS albums, SUM(album_type = ?) AS moments, SUM(album_type = ?) AS months, SUM(album_type = ?) AS states, SUM(album_type = ?) AS folders", entity.AlbumManual, entity.AlbumMoment, entity.AlbumMonth, entity.AlbumState, entity.AlbumFolder).
|
||||
Where("deleted_at IS NULL AND (albums.album_type <> 'folder' OR albums.album_path IN (SELECT photos.photo_path FROM photos WHERE photos.deleted_at IS NULL))").
|
||||
Take(&cfg.Count)
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ import (
|
|||
|
||||
const (
|
||||
AlbumUID = byte('a')
|
||||
AlbumDefault = "album"
|
||||
AlbumManual = "album"
|
||||
AlbumFolder = "folder"
|
||||
AlbumMoment = "moment"
|
||||
AlbumMonth = "month"
|
||||
|
@ -108,7 +108,7 @@ func AddPhotoToUserAlbums(photoUid string, albums []string, userUid string) (err
|
|||
if rnd.IsUID(album, AlbumUID) {
|
||||
albumUid = album
|
||||
} else {
|
||||
a := NewUserAlbum(album, AlbumDefault, userUid)
|
||||
a := NewUserAlbum(album, AlbumManual, userUid)
|
||||
|
||||
if found := a.Find(); found != nil {
|
||||
albumUid = found.AlbumUID
|
||||
|
@ -142,7 +142,7 @@ func NewUserAlbum(albumTitle, albumType, userUid string) *Album {
|
|||
|
||||
// Set default type.
|
||||
if albumType == "" {
|
||||
albumType = AlbumDefault
|
||||
albumType = AlbumManual
|
||||
}
|
||||
|
||||
// Set default values.
|
||||
|
@ -237,7 +237,7 @@ func NewMonthAlbum(albumTitle, albumSlug string, year, month int) *Album {
|
|||
albumTitle = strings.TrimSpace(albumTitle)
|
||||
albumSlug = strings.TrimSpace(albumSlug)
|
||||
|
||||
if albumTitle == "" || albumSlug == "" || year == 0 || month == 0 {
|
||||
if albumTitle == "" || albumSlug == "" || year < 1 || month < 1 || month > 12 {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -269,6 +269,10 @@ func NewMonthAlbum(albumTitle, albumSlug string, year, month int) *Album {
|
|||
func FindMonthAlbum(year, month int) *Album {
|
||||
m := Album{}
|
||||
|
||||
if year < 1 || month < 1 || month > 12 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if UnscopedDb().First(&m, "album_year = ? AND album_month = ? AND album_type = ?", year, month, AlbumMonth).RecordNotFound() {
|
||||
return nil
|
||||
}
|
||||
|
@ -280,6 +284,10 @@ func FindMonthAlbum(year, month int) *Album {
|
|||
func FindAlbumBySlug(albumSlug, albumType string) *Album {
|
||||
m := Album{}
|
||||
|
||||
if albumSlug == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
if UnscopedDb().First(&m, "album_slug = ? AND album_type = ?", albumSlug, albumType).RecordNotFound() {
|
||||
return nil
|
||||
}
|
||||
|
@ -291,13 +299,21 @@ func FindAlbumBySlug(albumSlug, albumType string) *Album {
|
|||
func FindAlbumByAttr(slugs, filters []string, albumType string) *Album {
|
||||
m := Album{}
|
||||
|
||||
if len(slugs) == 0 && len(filters) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
stmt := UnscopedDb()
|
||||
|
||||
if albumType != "" {
|
||||
stmt = stmt.Where("album_type = ?", albumType)
|
||||
}
|
||||
|
||||
if len(filters) == 0 {
|
||||
stmt = stmt.Where("album_slug IN (?)", slugs)
|
||||
} else {
|
||||
stmt = stmt.Where("album_slug IN (?) OR album_filter IN (?)", slugs, filters)
|
||||
}
|
||||
|
||||
if stmt.First(&m).RecordNotFound() {
|
||||
return nil
|
||||
|
@ -350,8 +366,12 @@ func FindAlbum(find Album) *Album {
|
|||
stmt := UnscopedDb().Where("album_type = ?", find.AlbumType)
|
||||
|
||||
// Search by slug and filter or title.
|
||||
if find.AlbumType != AlbumDefault && find.AlbumFilter != "" {
|
||||
if find.AlbumType != AlbumManual {
|
||||
if find.AlbumFilter != "" {
|
||||
stmt = stmt.Where("album_slug = ? OR album_filter = ?", find.AlbumSlug, find.AlbumFilter)
|
||||
} else {
|
||||
stmt = stmt.Where("album_slug = ?", find.AlbumSlug)
|
||||
}
|
||||
} else {
|
||||
stmt = stmt.Where("album_slug = ? OR album_title LIKE ?", find.AlbumSlug, find.AlbumTitle)
|
||||
}
|
||||
|
@ -374,6 +394,11 @@ func FindAlbum(find Album) *Album {
|
|||
return &m
|
||||
}
|
||||
|
||||
// HasID tests if the album has a valid id and uid.
|
||||
func (m *Album) HasID() bool {
|
||||
return m.ID > 0 && rnd.IsUID(m.AlbumUID, AlbumUID)
|
||||
}
|
||||
|
||||
// Find retrieves the matching record from the database and updates the entity.
|
||||
func (m *Album) Find() *Album {
|
||||
return FindAlbum(*m)
|
||||
|
@ -419,7 +444,7 @@ func (m *Album) IsState() bool {
|
|||
|
||||
// IsDefault tests if the album is a regular album.
|
||||
func (m *Album) IsDefault() bool {
|
||||
return m.AlbumType == AlbumDefault
|
||||
return m.AlbumType == AlbumManual
|
||||
}
|
||||
|
||||
// SetTitle changes the album name.
|
||||
|
@ -434,7 +459,7 @@ func (m *Album) SetTitle(title string) *Album {
|
|||
|
||||
m.AlbumTitle = title
|
||||
|
||||
if m.AlbumType == AlbumDefault || m.AlbumSlug == "" {
|
||||
if m.AlbumType == AlbumManual || m.AlbumSlug == "" {
|
||||
if len(m.AlbumTitle) < txt.ClipSlug {
|
||||
m.AlbumSlug = txt.Slug(m.AlbumTitle)
|
||||
} else {
|
||||
|
@ -581,8 +606,8 @@ func (m *Album) UpdateFolder(albumPath, albumFilter string) error {
|
|||
albumPath = strings.Trim(albumPath, string(os.PathSeparator))
|
||||
albumSlug := txt.Slug(albumPath)
|
||||
|
||||
if albumSlug == "" {
|
||||
return nil
|
||||
if albumSlug == "" || albumPath == "" || albumFilter == "" || !m.HasID() {
|
||||
return fmt.Errorf("folder album must have a path and filter")
|
||||
}
|
||||
|
||||
if err := UnscopedDb().Model(m).Updates(map[string]interface{}{
|
||||
|
@ -591,7 +616,7 @@ func (m *Album) UpdateFolder(albumPath, albumFilter string) error {
|
|||
"AlbumSlug": albumSlug,
|
||||
}).Error; err != nil {
|
||||
return err
|
||||
} else if err = UnscopedDb().Exec("UPDATE albums SET album_path = NULL WHERE album_path = ? AND id <> ?", albumPath, m.ID).Error; err != nil {
|
||||
} else if err = UnscopedDb().Exec("UPDATE albums SET album_path = NULL WHERE album_type = ? AND album_path = ? AND id <> ?", AlbumFolder, albumPath, m.ID).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -625,7 +650,7 @@ func (m *Album) PublishCountChange(n int) {
|
|||
data := event.Data{"count": n}
|
||||
|
||||
switch m.AlbumType {
|
||||
case AlbumDefault:
|
||||
case AlbumManual:
|
||||
event.Publish("count.albums", data)
|
||||
case AlbumMoment:
|
||||
event.Publish("count.moments", data)
|
||||
|
|
|
@ -11,7 +11,7 @@ func (m AlbumMap) Get(name string) Album {
|
|||
return result
|
||||
}
|
||||
|
||||
return *NewAlbum(name, AlbumDefault)
|
||||
return *NewAlbum(name, AlbumManual)
|
||||
}
|
||||
|
||||
func (m AlbumMap) Pointer(name string) *Album {
|
||||
|
@ -19,7 +19,7 @@ func (m AlbumMap) Pointer(name string) *Album {
|
|||
return &result
|
||||
}
|
||||
|
||||
return NewAlbum(name, AlbumDefault)
|
||||
return NewAlbum(name, AlbumManual)
|
||||
}
|
||||
|
||||
var AlbumFixtures = AlbumMap{
|
||||
|
@ -28,7 +28,7 @@ var AlbumFixtures = AlbumMap{
|
|||
AlbumUID: "at9lxuqxpogaaba7",
|
||||
AlbumSlug: "christmas-2030",
|
||||
AlbumPath: "",
|
||||
AlbumType: AlbumDefault,
|
||||
AlbumType: AlbumManual,
|
||||
AlbumTitle: "Christmas 2030",
|
||||
AlbumLocation: "",
|
||||
AlbumCategory: "",
|
||||
|
@ -53,7 +53,7 @@ var AlbumFixtures = AlbumMap{
|
|||
AlbumUID: "at9lxuqxpogaaba8",
|
||||
AlbumSlug: "holiday-2030",
|
||||
AlbumPath: "",
|
||||
AlbumType: AlbumDefault,
|
||||
AlbumType: AlbumManual,
|
||||
AlbumTitle: "Holiday 2030",
|
||||
AlbumLocation: "",
|
||||
AlbumCategory: "",
|
||||
|
@ -78,7 +78,7 @@ var AlbumFixtures = AlbumMap{
|
|||
AlbumUID: "at9lxuqxpogaaba9",
|
||||
AlbumSlug: "berlin-2019",
|
||||
AlbumPath: "",
|
||||
AlbumType: AlbumDefault,
|
||||
AlbumType: AlbumManual,
|
||||
AlbumTitle: "Berlin 2019",
|
||||
AlbumLocation: "Berlin",
|
||||
AlbumCategory: "City",
|
||||
|
@ -128,7 +128,7 @@ var AlbumFixtures = AlbumMap{
|
|||
AlbumUID: "at6axuzitogaaiax",
|
||||
AlbumSlug: "import",
|
||||
AlbumPath: "",
|
||||
AlbumType: AlbumDefault,
|
||||
AlbumType: AlbumManual,
|
||||
AlbumTitle: "Import Album",
|
||||
AlbumLocation: "",
|
||||
AlbumCategory: "",
|
||||
|
@ -298,7 +298,7 @@ var AlbumFixtures = AlbumMap{
|
|||
AlbumUID: "at1lxuqipotaab19",
|
||||
AlbumSlug: "&ilikefood",
|
||||
AlbumPath: "",
|
||||
AlbumType: AlbumDefault,
|
||||
AlbumType: AlbumManual,
|
||||
AlbumTitle: "&IlikeFood",
|
||||
AlbumLocation: "",
|
||||
AlbumCategory: "",
|
||||
|
@ -323,7 +323,7 @@ var AlbumFixtures = AlbumMap{
|
|||
AlbumUID: "at1lxuqipotaab20",
|
||||
AlbumSlug: "i-love-%-dog",
|
||||
AlbumPath: "",
|
||||
AlbumType: AlbumDefault,
|
||||
AlbumType: AlbumManual,
|
||||
AlbumTitle: "I love % dog",
|
||||
AlbumLocation: "",
|
||||
AlbumCategory: "",
|
||||
|
@ -348,7 +348,7 @@ var AlbumFixtures = AlbumMap{
|
|||
AlbumUID: "at1lxuqipotaab21",
|
||||
AlbumSlug: "%gold",
|
||||
AlbumPath: "",
|
||||
AlbumType: AlbumDefault,
|
||||
AlbumType: AlbumManual,
|
||||
AlbumTitle: "%gold",
|
||||
AlbumLocation: "",
|
||||
AlbumCategory: "",
|
||||
|
@ -373,7 +373,7 @@ var AlbumFixtures = AlbumMap{
|
|||
AlbumUID: "at1lxuqipotaab22",
|
||||
AlbumSlug: "sale%",
|
||||
AlbumPath: "",
|
||||
AlbumType: AlbumDefault,
|
||||
AlbumType: AlbumManual,
|
||||
AlbumTitle: "sale%",
|
||||
AlbumLocation: "",
|
||||
AlbumCategory: "",
|
||||
|
@ -398,7 +398,7 @@ var AlbumFixtures = AlbumMap{
|
|||
AlbumUID: "at1lxuqipotaab23",
|
||||
AlbumSlug: "pest&dogs",
|
||||
AlbumPath: "",
|
||||
AlbumType: AlbumDefault,
|
||||
AlbumType: AlbumManual,
|
||||
AlbumTitle: "Pets & Dogs",
|
||||
AlbumLocation: "",
|
||||
AlbumCategory: "",
|
||||
|
@ -423,7 +423,7 @@ var AlbumFixtures = AlbumMap{
|
|||
AlbumUID: "at1lxuqipotaab24",
|
||||
AlbumSlug: "light&",
|
||||
AlbumPath: "",
|
||||
AlbumType: AlbumDefault,
|
||||
AlbumType: AlbumManual,
|
||||
AlbumTitle: "Light&",
|
||||
AlbumLocation: "",
|
||||
AlbumCategory: "",
|
||||
|
@ -448,7 +448,7 @@ var AlbumFixtures = AlbumMap{
|
|||
AlbumUID: "at1lxuqipotaab25",
|
||||
AlbumSlug: "'family",
|
||||
AlbumPath: "",
|
||||
AlbumType: AlbumDefault,
|
||||
AlbumType: AlbumManual,
|
||||
AlbumTitle: "'Family",
|
||||
AlbumLocation: "",
|
||||
AlbumCategory: "",
|
||||
|
@ -473,7 +473,7 @@ var AlbumFixtures = AlbumMap{
|
|||
AlbumUID: "at1lxuqipotaab26",
|
||||
AlbumSlug: "father's-day",
|
||||
AlbumPath: "",
|
||||
AlbumType: AlbumDefault,
|
||||
AlbumType: AlbumManual,
|
||||
AlbumTitle: "Father's Day",
|
||||
AlbumLocation: "",
|
||||
AlbumCategory: "",
|
||||
|
@ -498,7 +498,7 @@ var AlbumFixtures = AlbumMap{
|
|||
AlbumUID: "at1lxuqipotaab27",
|
||||
AlbumSlug: "ice-cream'",
|
||||
AlbumPath: "",
|
||||
AlbumType: AlbumDefault,
|
||||
AlbumType: AlbumManual,
|
||||
AlbumTitle: "Ice Cream'",
|
||||
AlbumLocation: "",
|
||||
AlbumCategory: "",
|
||||
|
@ -523,7 +523,7 @@ var AlbumFixtures = AlbumMap{
|
|||
AlbumUID: "at1lxuqipotaab28",
|
||||
AlbumSlug: "*forrest",
|
||||
AlbumPath: "",
|
||||
AlbumType: AlbumDefault,
|
||||
AlbumType: AlbumManual,
|
||||
AlbumTitle: "*Forrest",
|
||||
AlbumLocation: "",
|
||||
AlbumCategory: "",
|
||||
|
@ -548,7 +548,7 @@ var AlbumFixtures = AlbumMap{
|
|||
AlbumUID: "at1lxuqipotaab29",
|
||||
AlbumSlug: "my*kids",
|
||||
AlbumPath: "",
|
||||
AlbumType: AlbumDefault,
|
||||
AlbumType: AlbumManual,
|
||||
AlbumTitle: "My*Kids",
|
||||
AlbumLocation: "",
|
||||
AlbumCategory: "",
|
||||
|
@ -573,7 +573,7 @@ var AlbumFixtures = AlbumMap{
|
|||
AlbumUID: "at1lxuqipotaab30",
|
||||
AlbumSlug: "yoga***",
|
||||
AlbumPath: "",
|
||||
AlbumType: AlbumDefault,
|
||||
AlbumType: AlbumManual,
|
||||
AlbumTitle: "Yoga***",
|
||||
AlbumLocation: "",
|
||||
AlbumCategory: "",
|
||||
|
@ -598,7 +598,7 @@ var AlbumFixtures = AlbumMap{
|
|||
AlbumUID: "at1lxuqipotaab31",
|
||||
AlbumSlug: "|banana",
|
||||
AlbumPath: "",
|
||||
AlbumType: AlbumDefault,
|
||||
AlbumType: AlbumManual,
|
||||
AlbumTitle: "|Banana",
|
||||
AlbumLocation: "",
|
||||
AlbumCategory: "",
|
||||
|
@ -623,7 +623,7 @@ var AlbumFixtures = AlbumMap{
|
|||
AlbumUID: "at1lxuqipotaab33",
|
||||
AlbumSlug: "blue|",
|
||||
AlbumPath: "",
|
||||
AlbumType: AlbumDefault,
|
||||
AlbumType: AlbumManual,
|
||||
AlbumTitle: "Blue|",
|
||||
AlbumLocation: "",
|
||||
AlbumCategory: "",
|
||||
|
@ -648,7 +648,7 @@ var AlbumFixtures = AlbumMap{
|
|||
AlbumUID: "at1lxuqipotaab34",
|
||||
AlbumSlug: "345-shirt",
|
||||
AlbumPath: "",
|
||||
AlbumType: AlbumDefault,
|
||||
AlbumType: AlbumManual,
|
||||
AlbumTitle: "345 Shirt",
|
||||
AlbumLocation: "",
|
||||
AlbumCategory: "",
|
||||
|
@ -673,7 +673,7 @@ var AlbumFixtures = AlbumMap{
|
|||
AlbumUID: "at1lxuqipotaab35",
|
||||
AlbumSlug: "color-555-blue",
|
||||
AlbumPath: "",
|
||||
AlbumType: AlbumDefault,
|
||||
AlbumType: AlbumManual,
|
||||
AlbumTitle: "Color555 Blue",
|
||||
AlbumLocation: "",
|
||||
AlbumCategory: "",
|
||||
|
@ -698,7 +698,7 @@ var AlbumFixtures = AlbumMap{
|
|||
AlbumUID: "at1lxuqipotaab36",
|
||||
AlbumSlug: "route-66",
|
||||
AlbumPath: "",
|
||||
AlbumType: AlbumDefault,
|
||||
AlbumType: AlbumManual,
|
||||
AlbumTitle: "Route 66",
|
||||
AlbumLocation: "",
|
||||
AlbumCategory: "",
|
||||
|
@ -723,7 +723,7 @@ var AlbumFixtures = AlbumMap{
|
|||
AlbumUID: "at1lxuqipotaab32",
|
||||
AlbumSlug: "red|green",
|
||||
AlbumPath: "",
|
||||
AlbumType: AlbumDefault,
|
||||
AlbumType: AlbumManual,
|
||||
AlbumTitle: "Red|Green",
|
||||
AlbumLocation: "",
|
||||
AlbumCategory: "",
|
||||
|
|
|
@ -13,12 +13,12 @@ import (
|
|||
|
||||
func TestNewAlbum(t *testing.T) {
|
||||
t.Run("name Christmas 2018", func(t *testing.T) {
|
||||
album := NewAlbum("Christmas 2018", AlbumDefault)
|
||||
album := NewAlbum("Christmas 2018", AlbumManual)
|
||||
assert.Equal(t, "Christmas 2018", album.AlbumTitle)
|
||||
assert.Equal(t, "christmas-2018", album.AlbumSlug)
|
||||
})
|
||||
t.Run("name empty", func(t *testing.T) {
|
||||
album := NewAlbum("", AlbumDefault)
|
||||
album := NewAlbum("", AlbumManual)
|
||||
|
||||
defaultName := time.Now().Format("January 2006")
|
||||
defaultSlug := txt.Slug(defaultName)
|
||||
|
@ -35,7 +35,7 @@ func TestNewAlbum(t *testing.T) {
|
|||
|
||||
func TestAlbum_SetName(t *testing.T) {
|
||||
t.Run("valid name", func(t *testing.T) {
|
||||
album := NewAlbum("initial name", AlbumDefault)
|
||||
album := NewAlbum("initial name", AlbumManual)
|
||||
assert.Equal(t, "initial name", album.AlbumTitle)
|
||||
assert.Equal(t, "initial-name", album.AlbumSlug)
|
||||
album.SetTitle("New Album \"Name\"")
|
||||
|
@ -43,7 +43,7 @@ func TestAlbum_SetName(t *testing.T) {
|
|||
assert.Equal(t, "new-album-name", album.AlbumSlug)
|
||||
})
|
||||
t.Run("empty name", func(t *testing.T) {
|
||||
album := NewAlbum("initial name", AlbumDefault)
|
||||
album := NewAlbum("initial name", AlbumManual)
|
||||
assert.Equal(t, "initial name", album.AlbumTitle)
|
||||
assert.Equal(t, "initial-name", album.AlbumSlug)
|
||||
|
||||
|
@ -63,7 +63,7 @@ The discrepancy of 1 second meridian arc length between equator and pole is abou
|
|||
is an oblate spheroid.`
|
||||
expected := txt.Shorten(longName, txt.ClipDefault, txt.Ellipsis)
|
||||
slugExpected := txt.Clip(longName, txt.ClipSlug)
|
||||
album := NewAlbum(longName, AlbumDefault)
|
||||
album := NewAlbum(longName, AlbumManual)
|
||||
assert.Equal(t, expected, album.AlbumTitle)
|
||||
assert.Contains(t, album.AlbumSlug, txt.Slug(slugExpected))
|
||||
})
|
||||
|
@ -128,7 +128,7 @@ func TestAlbum_UpdateState(t *testing.T) {
|
|||
|
||||
func TestAlbum_SaveForm(t *testing.T) {
|
||||
t.Run("success", func(t *testing.T) {
|
||||
album := NewAlbum("Old Name", AlbumDefault)
|
||||
album := NewAlbum("Old Name", AlbumManual)
|
||||
|
||||
assert.Equal(t, "Old Name", album.AlbumTitle)
|
||||
assert.Equal(t, "old-name", album.AlbumSlug)
|
||||
|
@ -284,7 +284,7 @@ func TestNewMonthAlbum(t *testing.T) {
|
|||
|
||||
func TestFindAlbumBySlug(t *testing.T) {
|
||||
t.Run("1 result", func(t *testing.T) {
|
||||
result := FindAlbumBySlug("holiday-2030", AlbumDefault)
|
||||
result := FindAlbumBySlug("holiday-2030", AlbumManual)
|
||||
|
||||
if result == nil {
|
||||
t.Fatal("album should not be nil")
|
||||
|
@ -317,7 +317,7 @@ func TestAlbum_String(t *testing.T) {
|
|||
album := Album{
|
||||
AlbumUID: "abc123",
|
||||
AlbumSlug: "test-slug",
|
||||
AlbumType: AlbumDefault,
|
||||
AlbumType: AlbumManual,
|
||||
AlbumTitle: "Test Title",
|
||||
}
|
||||
assert.Equal(t, "test-slug", album.String())
|
||||
|
@ -326,7 +326,7 @@ func TestAlbum_String(t *testing.T) {
|
|||
album := Album{
|
||||
AlbumUID: "abc123",
|
||||
AlbumSlug: "",
|
||||
AlbumType: AlbumDefault,
|
||||
AlbumType: AlbumManual,
|
||||
AlbumTitle: "Test Title",
|
||||
}
|
||||
assert.Contains(t, album.String(), "Test Title")
|
||||
|
@ -335,7 +335,7 @@ func TestAlbum_String(t *testing.T) {
|
|||
album := Album{
|
||||
AlbumUID: "abc123",
|
||||
AlbumSlug: "",
|
||||
AlbumType: AlbumDefault,
|
||||
AlbumType: AlbumManual,
|
||||
AlbumTitle: "",
|
||||
}
|
||||
assert.Equal(t, "abc123", album.String())
|
||||
|
@ -344,7 +344,7 @@ func TestAlbum_String(t *testing.T) {
|
|||
album := Album{
|
||||
AlbumUID: "",
|
||||
AlbumSlug: "",
|
||||
AlbumType: AlbumDefault,
|
||||
AlbumType: AlbumManual,
|
||||
AlbumTitle: "",
|
||||
}
|
||||
assert.Equal(t, "[unknown album]", album.String())
|
||||
|
@ -356,7 +356,7 @@ func TestAlbum_IsMoment(t *testing.T) {
|
|||
album := Album{
|
||||
AlbumUID: "abc123",
|
||||
AlbumSlug: "test-slug",
|
||||
AlbumType: AlbumDefault,
|
||||
AlbumType: AlbumManual,
|
||||
AlbumTitle: "Test Title",
|
||||
}
|
||||
assert.False(t, album.IsMoment())
|
||||
|
@ -377,7 +377,7 @@ func TestAlbum_Update(t *testing.T) {
|
|||
album := Album{
|
||||
AlbumUID: "abc123",
|
||||
AlbumSlug: "test-slug",
|
||||
AlbumType: AlbumDefault,
|
||||
AlbumType: AlbumManual,
|
||||
AlbumTitle: "Test Title",
|
||||
}
|
||||
assert.Equal(t, "test-slug", album.AlbumSlug)
|
||||
|
@ -413,7 +413,7 @@ func TestAlbum_Save(t *testing.T) {
|
|||
func TestAlbum_Create(t *testing.T) {
|
||||
t.Run("album", func(t *testing.T) {
|
||||
album := Album{
|
||||
AlbumType: AlbumDefault,
|
||||
AlbumType: AlbumManual,
|
||||
}
|
||||
|
||||
err := album.Create()
|
||||
|
@ -462,7 +462,7 @@ func TestAlbum_Title(t *testing.T) {
|
|||
album := Album{
|
||||
AlbumUID: "abc123",
|
||||
AlbumSlug: "test-slug",
|
||||
AlbumType: AlbumDefault,
|
||||
AlbumType: AlbumManual,
|
||||
AlbumTitle: "Test Title",
|
||||
}
|
||||
assert.Equal(t, "Test Title", album.Title())
|
||||
|
@ -482,7 +482,7 @@ func TestAlbum_AddPhotos(t *testing.T) {
|
|||
album := Album{
|
||||
AlbumUID: "abc123",
|
||||
AlbumSlug: "test-slug",
|
||||
AlbumType: AlbumDefault,
|
||||
AlbumType: AlbumManual,
|
||||
AlbumTitle: "Test Title",
|
||||
}
|
||||
added := album.AddPhotos([]string{"ab", "cd"})
|
||||
|
@ -495,7 +495,7 @@ func TestAlbum_RemovePhotos(t *testing.T) {
|
|||
album := Album{
|
||||
AlbumUID: "abc123",
|
||||
AlbumSlug: "test-slug",
|
||||
AlbumType: AlbumDefault,
|
||||
AlbumType: AlbumManual,
|
||||
AlbumTitle: "Test Title",
|
||||
}
|
||||
removed := album.RemovePhotos([]string{"ab", "cd"})
|
||||
|
@ -528,8 +528,8 @@ func TestAlbum_Find(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestAlbum_UpdateFolder(t *testing.T) {
|
||||
t.Run("success", func(t *testing.T) {
|
||||
a := Album{AlbumUID: "at6axuzitogaaxxx"}
|
||||
t.Run("Success", func(t *testing.T) {
|
||||
a := Album{ID: 99999, AlbumUID: "at6axuzitogaaxxx"}
|
||||
assert.Empty(t, a.AlbumPath)
|
||||
assert.Empty(t, a.AlbumFilter)
|
||||
if err := a.UpdateFolder("2222/07", "month:07"); err != nil {
|
||||
|
@ -538,16 +538,19 @@ func TestAlbum_UpdateFolder(t *testing.T) {
|
|||
assert.Equal(t, "2222/07", a.AlbumPath)
|
||||
assert.Equal(t, "month:07", a.AlbumFilter)
|
||||
})
|
||||
|
||||
t.Run("empty path", func(t *testing.T) {
|
||||
a := Album{AlbumUID: "at6axuzitogaaxxy"}
|
||||
t.Run("EmptyPath", func(t *testing.T) {
|
||||
a := Album{ID: 99999, AlbumUID: "at6axuzitogaaxxy"}
|
||||
assert.Empty(t, a.AlbumPath)
|
||||
assert.Empty(t, a.AlbumFilter)
|
||||
if err := a.UpdateFolder("", "month:07"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err := a.UpdateFolder("", "month:07")
|
||||
assert.Error(t, err)
|
||||
})
|
||||
t.Run("EmptyFilter", func(t *testing.T) {
|
||||
a := Album{ID: 99999, AlbumUID: "at6axuzitogaaxxy"}
|
||||
assert.Empty(t, a.AlbumPath)
|
||||
assert.Empty(t, a.AlbumFilter)
|
||||
err := a.UpdateFolder("2222/07", "")
|
||||
assert.Error(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -266,7 +266,7 @@ func (w *Moments) Start() (err error) {
|
|||
}
|
||||
|
||||
// Make sure that the albums have been backed up before, otherwise back up all albums.
|
||||
if fs.PathExists(filepath.Join(w.conf.AlbumsPath(), entity.AlbumDefault)) &&
|
||||
if fs.PathExists(filepath.Join(w.conf.AlbumsPath(), entity.AlbumManual)) &&
|
||||
fs.PathExists(filepath.Join(w.conf.AlbumsPath(), entity.AlbumMonth)) {
|
||||
// Skip.
|
||||
} else if count, err := BackupAlbums(w.conf.AlbumsPath(), false); err != nil {
|
||||
|
|
|
@ -10,6 +10,7 @@ import (
|
|||
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
"github.com/photoprism/photoprism/pkg/media"
|
||||
"github.com/photoprism/photoprism/pkg/rnd"
|
||||
"github.com/photoprism/photoprism/pkg/sortby"
|
||||
)
|
||||
|
||||
|
@ -21,17 +22,31 @@ func Albums(offset, limit int) (results entity.Albums, err error) {
|
|||
|
||||
// AlbumByUID returns a Album based on the UID.
|
||||
func AlbumByUID(albumUID string) (album entity.Album, err error) {
|
||||
if rnd.InvalidUID(albumUID, entity.AlbumUID) {
|
||||
return album, fmt.Errorf("invalid album uid")
|
||||
}
|
||||
|
||||
return entity.CachedAlbumByUID(albumUID)
|
||||
}
|
||||
|
||||
// AlbumCoverByUID returns an album cover file based on the uid.
|
||||
func AlbumCoverByUID(uid string, public bool) (file entity.File, err error) {
|
||||
if rnd.InvalidUID(uid, entity.AlbumUID) {
|
||||
return file, fmt.Errorf("invalid album uid")
|
||||
}
|
||||
|
||||
a := entity.Album{}
|
||||
|
||||
// Find album.
|
||||
if a, err = AlbumByUID(uid); err != nil {
|
||||
return file, err
|
||||
} else if a.AlbumType != entity.AlbumDefault { // TODO: Optimize
|
||||
} else if !a.HasID() {
|
||||
return file, fmt.Errorf("album uid %s is invalid", clean.Log(uid))
|
||||
} else if a.AlbumType != entity.AlbumManual { // TODO: Optimize
|
||||
if a.AlbumFilter == "" {
|
||||
return file, fmt.Errorf("smart album %s has no filter specified", a.AlbumUID)
|
||||
}
|
||||
|
||||
f := form.SearchPhotos{Album: a.AlbumUID, Filter: a.AlbumFilter, Order: sortby.Relevance, Count: 1, Offset: 0, Merged: false}
|
||||
|
||||
if err = f.ParseQueryString(); err != nil {
|
||||
|
@ -71,8 +86,8 @@ func AlbumCoverByUID(uid string, public bool) (file entity.File, err error) {
|
|||
|
||||
// Build query.
|
||||
stmt := Db().Where("files.file_primary = 1 AND files.file_missing = 0 AND files.file_type IN (?) AND files.deleted_at IS NULL", media.PreviewExpr).
|
||||
Joins("JOIN albums ON albums.album_uid = ?", uid).
|
||||
Joins("JOIN photos_albums pa ON pa.album_uid = albums.album_uid AND pa.photo_uid = files.photo_uid AND pa.hidden = 0").
|
||||
Joins("JOIN albums a ON a.album_uid = ?", uid).
|
||||
Joins("JOIN photos_albums pa ON pa.album_uid = a.album_uid AND pa.photo_uid = files.photo_uid AND pa.hidden = 0 AND pa.missing = 0").
|
||||
Joins("JOIN photos ON photos.id = files.photo_id AND photos.deleted_at IS NULL")
|
||||
|
||||
// Public pictures only?
|
||||
|
@ -81,7 +96,7 @@ func AlbumCoverByUID(uid string, public bool) (file entity.File, err error) {
|
|||
}
|
||||
|
||||
// Find first picture.
|
||||
if err := stmt.Order("photos.photo_quality DESC, photos.taken_at DESC").
|
||||
if err = stmt.Order("photos.photo_quality DESC, photos.taken_at DESC").
|
||||
First(&file).Error; err != nil {
|
||||
return file, err
|
||||
}
|
||||
|
@ -96,11 +111,11 @@ func UpdateAlbumDates() error {
|
|||
|
||||
switch DbDialect() {
|
||||
case MySQL:
|
||||
return UnscopedDb().Exec(`UPDATE albums
|
||||
INNER JOIN
|
||||
(SELECT photo_path, MAX(taken_at_local) AS taken_max
|
||||
return UnscopedDb().Exec(`UPDATE albums INNER JOIN (
|
||||
SELECT photo_path, MAX(taken_at_local) AS taken_max
|
||||
FROM photos WHERE taken_src = 'meta' AND photos.photo_quality >= 3 AND photos.deleted_at IS NULL
|
||||
GROUP BY photo_path) AS p ON albums.album_path = p.photo_path
|
||||
GROUP BY photo_path
|
||||
) AS p ON albums.album_path = p.photo_path
|
||||
SET albums.album_year = YEAR(taken_max), albums.album_month = MONTH(taken_max), albums.album_day = DAY(taken_max)
|
||||
WHERE albums.album_type = 'folder' AND albums.album_path IS NOT NULL AND p.taken_max IS NOT NULL`).Error
|
||||
default:
|
||||
|
@ -122,6 +137,10 @@ func UpdateMissingAlbumEntries() error {
|
|||
|
||||
// AlbumEntryFound removes the missing flag from album entries.
|
||||
func AlbumEntryFound(uid string) error {
|
||||
if rnd.InvalidUID(uid, entity.PhotoUID) {
|
||||
return fmt.Errorf("invalid photo uid")
|
||||
}
|
||||
|
||||
switch DbDialect() {
|
||||
default:
|
||||
return UnscopedDb().Exec(`UPDATE photos_albums SET missing = 0 WHERE photo_uid = ?`, uid).Error
|
||||
|
@ -131,6 +150,12 @@ func AlbumEntryFound(uid string) error {
|
|||
// AlbumsPhotoUIDs returns up to 100000 photo UIDs that belong to the specified albums.
|
||||
func AlbumsPhotoUIDs(albums []string, includeDefault, includePrivate bool) (photos []string, err error) {
|
||||
for _, albumUid := range albums {
|
||||
if rnd.InvalidUID(albumUid, entity.AlbumUID) {
|
||||
// Should never happen.
|
||||
log.Debugf("query: album uid %s is invalid", clean.Log(albumUid))
|
||||
continue
|
||||
}
|
||||
|
||||
a, err := AlbumByUID(albumUid)
|
||||
|
||||
if err != nil {
|
||||
|
@ -138,7 +163,11 @@ func AlbumsPhotoUIDs(albums []string, includeDefault, includePrivate bool) (phot
|
|||
continue
|
||||
}
|
||||
|
||||
if a.IsDefault() && !includeDefault {
|
||||
if a.IsDefault() && !includeDefault || !a.HasID() {
|
||||
continue
|
||||
} else if !a.IsDefault() && a.AlbumFilter == "" {
|
||||
// Should never happen.
|
||||
log.Debugf("query: smart album %s has empty filter", clean.Log(a.AlbumUID))
|
||||
continue
|
||||
}
|
||||
|
||||
|
|
|
@ -47,7 +47,7 @@ func (c *Counts) Refresh() {
|
|||
Take(c)
|
||||
|
||||
Db().Table("albums").
|
||||
Select("SUM(album_type = ?) AS albums, SUM(album_type = ?) AS moments, SUM(album_type = ?) AS folders", entity.AlbumDefault, entity.AlbumMoment, entity.AlbumFolder).
|
||||
Select("SUM(album_type = ?) AS albums, SUM(album_type = ?) AS moments, SUM(album_type = ?) AS folders", entity.AlbumManual, entity.AlbumMoment, entity.AlbumFolder).
|
||||
Where("deleted_at IS NULL").
|
||||
Take(c)
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ func UpdateAlbumDefaultCovers() (err error) {
|
|||
|
||||
var res *gorm.DB
|
||||
|
||||
condition := gorm.Expr("album_type = ? AND thumb_src = ?", entity.AlbumDefault, entity.SrcAuto)
|
||||
condition := gorm.Expr("album_type = ? AND thumb_src = ?", entity.AlbumManual, entity.SrcAuto)
|
||||
|
||||
switch DbDialect() {
|
||||
case MySQL:
|
||||
|
|
|
@ -187,20 +187,20 @@ type Moments []Moment
|
|||
|
||||
// MomentsTime counts photos by month and year.
|
||||
func MomentsTime(threshold int, public bool) (results Moments, err error) {
|
||||
db := UnscopedDb().Table("photos").
|
||||
stmt := UnscopedDb().Table("photos").
|
||||
Select("photos.photo_year AS year, photos.photo_month AS month, COUNT(*) AS photo_count").
|
||||
Where("photos.photo_quality >= 3 AND deleted_at IS NULL AND photos.photo_year > 0 AND photos.photo_month > 0")
|
||||
|
||||
// Ignore private pictures?
|
||||
if public {
|
||||
db = db.Where("photo_private = 0")
|
||||
stmt = stmt.Where("photo_private = 0")
|
||||
}
|
||||
|
||||
db = db.Group("photos.photo_year, photos.photo_month").
|
||||
stmt = stmt.Group("photos.photo_year, photos.photo_month").
|
||||
Order("photos.photo_year DESC, photos.photo_month DESC").
|
||||
Having("photo_count >= ?", threshold)
|
||||
|
||||
if err := db.Scan(&results).Error; err != nil {
|
||||
if err = stmt.Scan(&results).Error; err != nil {
|
||||
return results, err
|
||||
}
|
||||
|
||||
|
@ -209,19 +209,19 @@ func MomentsTime(threshold int, public bool) (results Moments, err error) {
|
|||
|
||||
// MomentsCountries returns the most popular countries by year.
|
||||
func MomentsCountries(threshold int, public bool) (results Moments, err error) {
|
||||
db := UnscopedDb().Table("photos").
|
||||
stmt := UnscopedDb().Table("photos").
|
||||
Select("photo_year AS year, photo_country AS country, COUNT(*) AS photo_count").
|
||||
Where("photos.photo_quality >= 3 AND deleted_at IS NULL AND photo_country <> 'zz' AND photo_year > 0")
|
||||
|
||||
// Ignore private pictures?
|
||||
if public {
|
||||
db = db.Where("photo_private = 0")
|
||||
stmt = stmt.Where("photo_private = 0")
|
||||
}
|
||||
|
||||
db = db.Group("photo_year, photo_country").
|
||||
stmt = stmt.Group("photo_year, photo_country").
|
||||
Having("photo_count >= ?", threshold)
|
||||
|
||||
if err := db.Scan(&results).Error; err != nil {
|
||||
if err = stmt.Scan(&results).Error; err != nil {
|
||||
return results, err
|
||||
}
|
||||
|
||||
|
@ -230,20 +230,20 @@ func MomentsCountries(threshold int, public bool) (results Moments, err error) {
|
|||
|
||||
// MomentsStates returns the most popular states and countries by year.
|
||||
func MomentsStates(threshold int, public bool) (results Moments, err error) {
|
||||
db := UnscopedDb().Table("photos").
|
||||
stmt := UnscopedDb().Table("photos").
|
||||
Select("p.place_country AS country, p.place_state AS state, COUNT(*) AS photo_count").
|
||||
Joins("JOIN places p ON p.id = photos.place_id").
|
||||
Where("photos.photo_quality >= 3 AND photos.deleted_at IS NULL AND p.place_state <> '' AND p.place_country <> 'zz'")
|
||||
|
||||
// Ignore private pictures?
|
||||
if public {
|
||||
db = db.Where("photo_private = 0")
|
||||
stmt = stmt.Where("photo_private = 0")
|
||||
}
|
||||
|
||||
db = db.Group("p.place_country, p.place_state").
|
||||
stmt = stmt.Group("p.place_country, p.place_state").
|
||||
Having("photo_count >= ?", threshold)
|
||||
|
||||
if err := db.Scan(&results).Error; err != nil {
|
||||
if err = stmt.Scan(&results).Error; err != nil {
|
||||
return results, err
|
||||
}
|
||||
|
||||
|
@ -260,7 +260,7 @@ func MomentsLabels(threshold int, public bool) (results Moments, err error) {
|
|||
|
||||
m := Moments{}
|
||||
|
||||
db := UnscopedDb().Table("photos").
|
||||
stmt := UnscopedDb().Table("photos").
|
||||
Select("l.label_slug AS label, COUNT(*) AS photo_count").
|
||||
Joins("JOIN photos_labels pl ON pl.photo_id = photos.id AND pl.uncertainty < 100").
|
||||
Joins("JOIN labels l ON l.id = pl.label_id").
|
||||
|
@ -268,13 +268,13 @@ func MomentsLabels(threshold int, public bool) (results Moments, err error) {
|
|||
|
||||
// Ignore private pictures?
|
||||
if public {
|
||||
db = db.Where("photo_private = 0")
|
||||
stmt = stmt.Where("photo_private = 0")
|
||||
}
|
||||
|
||||
db = db.Group("l.label_slug").
|
||||
stmt = stmt.Group("l.label_slug").
|
||||
Having("photo_count >= ?", threshold)
|
||||
|
||||
if err := db.Scan(&m).Error; err != nil {
|
||||
if err = stmt.Scan(&m).Error; err != nil {
|
||||
return m, err
|
||||
}
|
||||
|
||||
|
@ -299,16 +299,18 @@ func MomentsLabels(threshold int, public bool) (results Moments, err error) {
|
|||
// RemoveDuplicateMoments deletes generated albums with duplicate slug or filter.
|
||||
func RemoveDuplicateMoments() (removed int, err error) {
|
||||
if res := UnscopedDb().Exec(`DELETE FROM links WHERE share_uid
|
||||
IN (SELECT a.album_uid FROM albums a JOIN albums b ON a.album_type = b.album_type
|
||||
AND a.album_type <> ? AND a.id > b.id WHERE (a.album_slug = b.album_slug
|
||||
OR a.album_filter = b.album_filter) GROUP BY a.album_uid)`, entity.AlbumDefault); res.Error != nil {
|
||||
IN (SELECT a.album_uid FROM albums a JOIN albums b ON a.album_type <> ?
|
||||
AND a.album_type = b.album_type AND a.id > b.id
|
||||
WHERE (a.album_slug = b.album_slug OR a.album_filter = b.album_filter)
|
||||
GROUP BY a.album_uid)`, entity.AlbumManual); res.Error != nil {
|
||||
return removed, res.Error
|
||||
}
|
||||
|
||||
if res := UnscopedDb().Exec(`DELETE FROM albums WHERE id
|
||||
IN (SELECT a.id FROM albums a JOIN albums b ON a.album_type = b.album_type
|
||||
AND a.album_type <> ? AND a.id > b.id WHERE (a.album_slug = b.album_slug
|
||||
OR a.album_filter = b.album_filter) GROUP BY a.album_uid)`, entity.AlbumDefault); res.Error != nil {
|
||||
IN (SELECT a.id FROM albums a JOIN albums b ON a.album_type <> ?
|
||||
AND a.album_type = b.album_type AND a.id > b.id
|
||||
WHERE (a.album_slug = b.album_slug OR a.album_filter = b.album_filter)
|
||||
GROUP BY a.album_uid)`, entity.AlbumManual); res.Error != nil {
|
||||
return removed, res.Error
|
||||
} else if res.RowsAffected > 0 {
|
||||
removed = int(res.RowsAffected)
|
||||
|
|
|
@ -44,7 +44,7 @@ func UserAlbums(f form.SearchAlbums, sess *entity.Session) (results AlbumResults
|
|||
// Determine resource to check.
|
||||
var aclResource acl.Resource
|
||||
switch f.Type {
|
||||
case entity.AlbumDefault:
|
||||
case entity.AlbumManual:
|
||||
aclResource = acl.ResourceAlbums
|
||||
case entity.AlbumFolder:
|
||||
aclResource = acl.ResourceFolders
|
||||
|
@ -85,7 +85,7 @@ func UserAlbums(f form.SearchAlbums, sess *entity.Session) (results AlbumResults
|
|||
case sortby.Count:
|
||||
s = s.Order("photo_count DESC, albums.album_title, albums.album_uid DESC")
|
||||
case sortby.Moment, sortby.Newest:
|
||||
if f.Type == entity.AlbumDefault || f.Type == entity.AlbumState {
|
||||
if f.Type == entity.AlbumManual || f.Type == entity.AlbumState {
|
||||
s = s.Order("albums.album_uid DESC")
|
||||
} else if f.Type == entity.AlbumMoment {
|
||||
s = s.Order("has_year, albums.album_year DESC, albums.album_month DESC, albums.album_day DESC, albums.album_title, albums.album_uid DESC")
|
||||
|
@ -93,7 +93,7 @@ func UserAlbums(f form.SearchAlbums, sess *entity.Session) (results AlbumResults
|
|||
s = s.Order("albums.album_year DESC, albums.album_month DESC, albums.album_day DESC, albums.album_title, albums.album_uid DESC")
|
||||
}
|
||||
case sortby.Oldest:
|
||||
if f.Type == entity.AlbumDefault || f.Type == entity.AlbumState {
|
||||
if f.Type == entity.AlbumManual || f.Type == entity.AlbumState {
|
||||
s = s.Order("albums.album_uid ASC")
|
||||
} else if f.Type == entity.AlbumMoment {
|
||||
s = s.Order("has_year, albums.album_year ASC, albums.album_month ASC, albums.album_day ASC, albums.album_title, albums.album_uid ASC")
|
||||
|
@ -190,7 +190,7 @@ func UserAlbums(f form.SearchAlbums, sess *entity.Session) (results AlbumResults
|
|||
if txt.NotEmpty(f.Year) {
|
||||
// Filter by the pictures included if it is a manually managed album, as these do not have an explicit
|
||||
// year assigned to them, unlike calendar albums and moments for example.
|
||||
if f.Type == entity.AlbumDefault {
|
||||
if f.Type == entity.AlbumManual {
|
||||
s = s.Where("? OR albums.album_uid IN (SELECT DISTINCT pay.album_uid FROM photos_albums pay "+
|
||||
"JOIN photos py ON pay.photo_uid = py.photo_uid WHERE py.photo_year IN (?) AND pay.hidden = 0 AND pay.missing = 0)",
|
||||
gorm.Expr(AnyInt("albums.album_year", f.Year, txt.Or, entity.UnknownYear, txt.YearMax)), strings.Split(f.Year, txt.Or))
|
||||
|
|
|
@ -156,7 +156,7 @@ func TestAlbums(t *testing.T) {
|
|||
})
|
||||
t.Run("SearchAlbumForYear", func(t *testing.T) {
|
||||
f := form.SearchAlbums{
|
||||
Type: entity.AlbumDefault,
|
||||
Type: entity.AlbumManual,
|
||||
Year: "2018",
|
||||
Month: "",
|
||||
Day: "",
|
||||
|
|
Loading…
Reference in a new issue