2021-09-18 15:32:39 +02:00
package search
import (
"strings"
2022-09-30 19:15:10 +02:00
"time"
2021-09-18 15:32:39 +02:00
2022-09-30 19:15:10 +02:00
"github.com/dustin/go-humanize/english"
"github.com/photoprism/photoprism/pkg/rnd"
"github.com/photoprism/photoprism/internal/acl"
2021-09-18 15:32:39 +02:00
"github.com/photoprism/photoprism/internal/entity"
"github.com/photoprism/photoprism/internal/form"
"github.com/photoprism/photoprism/pkg/txt"
)
2022-09-30 19:15:10 +02:00
// Albums finds AlbumResults based on the search form without checking rights or permissions.
2021-11-26 14:28:50 +01:00
func Albums ( f form . SearchAlbums ) ( results AlbumResults , err error ) {
2022-09-30 19:15:10 +02:00
return UserAlbums ( f , nil )
}
// UserAlbums finds AlbumResults based on the search form and user session.
func UserAlbums ( f form . SearchAlbums , sess * entity . Session ) ( results AlbumResults , err error ) {
start := time . Now ( )
if err = f . ParseQueryString ( ) ; err != nil {
log . Debugf ( "albums: %s" , err )
return AlbumResults { } , err
2021-09-18 15:32:39 +02:00
}
// Base query.
s := UnscopedDb ( ) . Table ( "albums" ) .
2021-11-18 00:46:34 +01:00
Select ( "albums.*, cp.photo_count, cl.link_count, CASE WHEN albums.album_year = 0 THEN 0 ELSE 1 END AS has_year" ) .
2021-09-18 15:32:39 +02:00
Joins ( "LEFT JOIN (SELECT album_uid, count(photo_uid) AS photo_count FROM photos_albums WHERE hidden = 0 AND missing = 0 GROUP BY album_uid) AS cp ON cp.album_uid = albums.album_uid" ) .
Joins ( "LEFT JOIN (SELECT share_uid, count(share_uid) AS link_count FROM links GROUP BY share_uid) AS cl ON cl.share_uid = albums.album_uid" ) .
Where ( "albums.deleted_at IS NULL" )
2022-09-30 19:15:10 +02:00
// Check session permissions and apply as needed.
if sess != nil {
user := sess . User ( )
aclRole := user . AclRole ( )
// Determine resource to check.
var aclResource acl . Resource
switch f . Type {
case entity . AlbumDefault :
aclResource = acl . ResourceAlbums
case entity . AlbumFolder :
aclResource = acl . ResourceFolders
case entity . AlbumMoment :
aclResource = acl . ResourceMoments
case entity . AlbumMonth :
aclResource = acl . ResourceCalendar
case entity . AlbumState :
aclResource = acl . ResourcePlaces
2021-11-18 12:54:26 +01:00
}
2022-10-02 22:09:02 +02:00
// Check user permissions.
2022-09-30 19:15:10 +02:00
if acl . Resources . DenyAll ( aclResource , aclRole , acl . Permissions { acl . AccessAll , acl . AccessLibrary , acl . AccessShared , acl . AccessOwn } ) {
return AlbumResults { } , ErrForbidden
}
2021-11-18 12:54:26 +01:00
2022-09-30 19:15:10 +02:00
// Limit results by UID, owner and path.
2022-10-02 22:09:02 +02:00
if sess . IsVisitor ( ) || sess . NotRegistered ( ) {
s = s . Where ( "albums.album_uid IN (?) OR albums.published_at > ?" , sess . SharedUIDs ( ) , entity . TimeStamp ( ) )
2022-09-30 19:15:10 +02:00
} else if acl . Resources . DenyAll ( aclResource , aclRole , acl . Permissions { acl . AccessAll , acl . AccessLibrary } ) {
if user . BasePath == "" {
2022-10-02 22:09:02 +02:00
s = s . Where ( "albums.album_uid IN (?) OR albums.created_by = ? OR albums.published_at > ?" , sess . SharedUIDs ( ) , user . UserUID , entity . TimeStamp ( ) )
2022-09-30 19:15:10 +02:00
} else {
2022-10-02 22:09:02 +02:00
s = s . Where ( "albums.album_uid IN (?) OR albums.created_by = ? OR albums.published_at > ? OR albums.album_type = ? AND (albums.album_path = ? OR albums.album_path LIKE ?)" ,
sess . SharedUIDs ( ) , user . UserUID , entity . TimeStamp ( ) , entity . AlbumFolder , user . BasePath , user . BasePath + "/%" )
2021-11-18 12:54:26 +01:00
}
2022-09-30 19:15:10 +02:00
}
2021-11-18 12:54:26 +01:00
2022-09-30 19:15:10 +02:00
// Exclude private content?
if acl . Resources . Deny ( acl . ResourcePhotos , aclRole , acl . AccessPrivate ) || acl . Resources . Deny ( aclResource , aclRole , acl . AccessPrivate ) {
f . Public = true
f . Private = false
2021-11-18 12:54:26 +01:00
}
}
2021-09-18 15:32:39 +02:00
// Set sort order.
switch f . Order {
2021-11-18 00:46:34 +01:00
case entity . SortOrderCount :
s = s . Order ( "photo_count DESC, albums.album_title, albums.album_uid DESC" )
case entity . SortOrderRelevance :
s = s . Order ( "albums.album_favorite DESC, albums.updated_at DESC, albums.album_uid DESC" )
case entity . SortOrderNewest :
s = s . Order ( "albums.album_favorite DESC, albums.album_year DESC, albums.album_month DESC, albums.album_day DESC, albums.album_title, albums.album_uid DESC" )
case entity . SortOrderOldest :
s = s . Order ( "albums.album_favorite DESC, albums.album_year ASC, albums.album_month ASC, albums.album_day ASC, albums.album_title, albums.album_uid ASC" )
case entity . SortOrderAdded :
s = s . Order ( "albums.album_uid DESC" )
case entity . SortOrderMoment :
s = s . Order ( "albums.album_favorite DESC, has_year, albums.album_year DESC, albums.album_month DESC, albums.album_title ASC, albums.album_uid DESC" )
case entity . SortOrderPlace :
2021-11-20 16:36:34 +01:00
s = s . Order ( "albums.album_favorite DESC, albums.album_location, albums.album_title, albums.album_year DESC, albums.album_month ASC, albums.album_day ASC, albums.album_uid DESC" )
2021-11-18 00:46:34 +01:00
case entity . SortOrderPath :
2021-11-18 12:54:26 +01:00
s = s . Order ( "albums.album_path, albums.album_uid DESC" )
2021-11-18 00:46:34 +01:00
case entity . SortOrderCategory :
s = s . Order ( "albums.album_category, albums.album_title, albums.album_uid DESC" )
case entity . SortOrderSlug :
s = s . Order ( "albums.album_favorite DESC, albums.album_slug ASC, albums.album_uid DESC" )
2021-11-29 14:48:42 +01:00
case entity . SortOrderName :
s = s . Order ( "albums.album_favorite DESC, albums.album_title ASC, albums.album_uid DESC" )
2021-09-18 15:32:39 +02:00
default :
2021-11-29 14:48:42 +01:00
s = s . Order ( "albums.album_favorite DESC, albums.album_title ASC, albums.album_uid DESC" )
2021-09-18 15:32:39 +02:00
}
2022-09-30 19:15:10 +02:00
// Find specific UIDs only?
if txt . NotEmpty ( f . UID ) {
ids := SplitOr ( strings . ToLower ( f . UID ) )
if rnd . ContainsUID ( ids , entity . AlbumUID ) {
s = s . Where ( "albums.album_uid IN (?)" , ids )
}
}
// Filter by title or path?
if txt . NotEmpty ( f . Query ) {
if f . Type != entity . AlbumFolder {
likeString := "%" + f . Query + "%"
s = s . Where ( "albums.album_title LIKE ? OR albums.album_location LIKE ?" , likeString , likeString )
} else {
f . Order = entity . SortOrderPath
p := f . Query
if strings . HasPrefix ( p , "/" ) {
p = p [ 1 : ]
}
2021-09-18 15:32:39 +02:00
2022-09-30 19:15:10 +02:00
if strings . HasSuffix ( p , "/" ) {
s = s . Where ( "albums.album_path = ?" , p [ : len ( p ) - 1 ] )
} else {
p = p + "*"
where , values := OrLike ( "albums.album_path" , p )
if w , v := OrLike ( "albums.album_title" , p ) ; len ( v ) > 0 {
where = where + " OR " + w
values = append ( values , v ... )
}
s = s . Where ( where , values ... )
}
2021-09-18 15:32:39 +02:00
}
2022-09-30 19:15:10 +02:00
}
2021-09-18 15:32:39 +02:00
2022-09-30 19:15:10 +02:00
// Albums with public pictures only?
if f . Public {
s = s . Where ( "albums.album_type <> 'folder' OR albums.album_path IN (SELECT photo_path FROM photos WHERE photo_private = 0 AND photo_quality > -1 AND deleted_at IS NULL)" )
} else {
s = s . Where ( "albums.album_type <> 'folder' OR albums.album_path IN (SELECT photo_path FROM photos WHERE photo_quality > -1 AND deleted_at IS NULL)" )
2021-09-18 15:32:39 +02:00
}
2022-09-30 19:15:10 +02:00
if txt . NotEmpty ( f . Type ) {
2021-09-18 15:32:39 +02:00
s = s . Where ( "albums.album_type IN (?)" , strings . Split ( f . Type , txt . Or ) )
}
2022-09-30 19:15:10 +02:00
if txt . NotEmpty ( f . Category ) {
2021-09-18 15:32:39 +02:00
s = s . Where ( "albums.album_category IN (?)" , strings . Split ( f . Category , txt . Or ) )
}
2022-09-30 19:15:10 +02:00
if txt . NotEmpty ( f . Location ) {
2021-09-18 15:32:39 +02:00
s = s . Where ( "albums.album_location IN (?)" , strings . Split ( f . Location , txt . Or ) )
}
2022-09-30 19:15:10 +02:00
if txt . NotEmpty ( f . Country ) {
2021-09-18 15:32:39 +02:00
s = s . Where ( "albums.album_country IN (?)" , strings . Split ( f . Country , txt . Or ) )
}
if f . Favorite {
s = s . Where ( "albums.album_favorite = 1" )
}
if ( f . Year > 0 && f . Year <= txt . YearMax ) || f . Year == entity . UnknownYear {
s = s . Where ( "albums.album_year = ?" , f . Year )
}
if ( f . Month >= txt . MonthMin && f . Month <= txt . MonthMax ) || f . Month == entity . UnknownMonth {
s = s . Where ( "albums.album_month = ?" , f . Month )
}
if ( f . Day >= txt . DayMin && f . Month <= txt . DayMax ) || f . Day == entity . UnknownDay {
s = s . Where ( "albums.album_day = ?" , f . Day )
}
2022-09-30 19:15:10 +02:00
// Limit result count.
if f . Count > 0 && f . Count <= MaxResults {
s = s . Limit ( f . Count ) . Offset ( f . Offset )
} else {
s = s . Limit ( MaxResults ) . Offset ( f . Offset )
}
// Query database.
2021-09-18 15:32:39 +02:00
if result := s . Scan ( & results ) ; result . Error != nil {
return results , result . Error
}
2022-09-30 19:15:10 +02:00
// Log number of results.
log . Debugf ( "albums: found %s [%s]" , english . Plural ( len ( results ) , "result" , "results" ) , time . Since ( start ) )
2021-09-18 15:32:39 +02:00
return results , nil
}