176 lines
5.2 KiB
Go
176 lines
5.2 KiB
Go
package useCases
|
|
|
|
import (
|
|
"RenewCMS/internal/domain/article"
|
|
"RenewCMS/internal/domain/author"
|
|
"RenewCMS/internal/domain/contenttype"
|
|
"RenewCMS/internal/domain/entry"
|
|
"RenewCMS/internal/domain/image"
|
|
"RenewCMS/internal/domain/site"
|
|
"slices"
|
|
)
|
|
|
|
const (
|
|
UsageTypeArticle = "article"
|
|
UsageTypeAuthor = "author"
|
|
UsageTypeSite = "site"
|
|
UsageTypeEntry = "entry"
|
|
|
|
UsageFieldBody = "body"
|
|
UsageFieldCover = "cover"
|
|
UsageFieldSocial = "social"
|
|
UsageFieldAvatar = "avatar"
|
|
UsageFieldDefaultSocialImage = "default_social_image"
|
|
)
|
|
|
|
type MediaUsage struct {
|
|
Type string
|
|
ID uint32
|
|
Title string
|
|
Published *bool
|
|
Fields []string
|
|
|
|
ContentTypeKey string
|
|
ContentTypeName string
|
|
FirstField *UsageFirstField
|
|
}
|
|
|
|
type UsageFirstField struct {
|
|
Key string
|
|
Type contenttype.FieldType
|
|
Value any
|
|
}
|
|
|
|
type GetMediaUsageUseCase struct {
|
|
imageRepository image.Repository
|
|
articleRepository article.Repository
|
|
sites site.Repository
|
|
authors author.Repository
|
|
entries *entryMediaScanner
|
|
}
|
|
|
|
func NewGetMediaUsageUseCase(imageRepository image.Repository, articleRepository article.Repository, sites site.Repository, authors author.Repository, contentTypes contenttype.Repository, entries entry.Repository) *GetMediaUsageUseCase {
|
|
return &GetMediaUsageUseCase{imageRepository: imageRepository, articleRepository: articleRepository, sites: sites, authors: authors, entries: newEntryMediaScanner(contentTypes, entries)}
|
|
}
|
|
|
|
func (u *GetMediaUsageUseCase) Usage(id uint32) ([]MediaUsage, error) {
|
|
img, err := u.imageRepository.Get(id)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
usages, err := u.articleUsages(img)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
authorUsages, err := u.authorUsages(img)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
siteUsages, err := u.siteUsages(img)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
entryUsages, err := u.entryUsages(img)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return slices.Concat(usages, authorUsages, siteUsages, entryUsages), nil
|
|
}
|
|
|
|
func (u *GetMediaUsageUseCase) entryUsages(img image.Image) ([]MediaUsage, error) {
|
|
referencing, err := u.entries.referencing(img.ID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
targets := map[uint32]bool{img.ID: true}
|
|
usages := make([]MediaUsage, 0, len(referencing))
|
|
for _, found := range referencing {
|
|
first := found.owner.Fields[0]
|
|
usages = append(usages, MediaUsage{
|
|
Type: UsageTypeEntry,
|
|
ID: found.record.ID,
|
|
Fields: found.keysPointingAt(targets),
|
|
ContentTypeKey: found.owner.Key,
|
|
ContentTypeName: found.owner.Name,
|
|
FirstField: &UsageFirstField{Key: first.Key, Type: first.Type, Value: found.record.Data[first.Key]},
|
|
})
|
|
}
|
|
return usages, nil
|
|
}
|
|
|
|
func (u *GetMediaUsageUseCase) articleUsages(img image.Image) ([]MediaUsage, error) {
|
|
storedName := image.StoredName(img.Path)
|
|
inBody, err := u.articleRepository.FindByBodyContaining(storedName)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
referencing, err := u.articleRepository.FindByImage(img.ID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
byID := map[uint32]*MediaUsage{}
|
|
for _, found := range inBody {
|
|
if !image.ContainsStoredName(found.Body, storedName) {
|
|
continue
|
|
}
|
|
entry := usageFor(byID, found)
|
|
entry.Fields = append(entry.Fields, UsageFieldBody)
|
|
}
|
|
for _, found := range referencing {
|
|
entry := usageFor(byID, found)
|
|
if found.CoverImageID != nil && *found.CoverImageID == img.ID {
|
|
entry.Fields = append(entry.Fields, UsageFieldCover)
|
|
}
|
|
if found.SocialImageID != nil && *found.SocialImageID == img.ID {
|
|
entry.Fields = append(entry.Fields, UsageFieldSocial)
|
|
}
|
|
}
|
|
usages := make([]MediaUsage, 0, len(byID))
|
|
for _, entry := range byID {
|
|
usages = append(usages, *entry)
|
|
}
|
|
slices.SortFunc(usages, func(a, b MediaUsage) int { return int(a.ID) - int(b.ID) })
|
|
return usages, nil
|
|
}
|
|
|
|
func usageFor(byID map[uint32]*MediaUsage, found article.Article) *MediaUsage {
|
|
if entry, ok := byID[found.ID]; ok {
|
|
return entry
|
|
}
|
|
published := found.IsOnline
|
|
entry := &MediaUsage{Type: UsageTypeArticle, ID: found.ID, Title: found.Title, Published: &published, Fields: []string{}}
|
|
byID[found.ID] = entry
|
|
return entry
|
|
}
|
|
|
|
func (u *GetMediaUsageUseCase) authorUsages(img image.Image) ([]MediaUsage, error) {
|
|
found, err := u.authors.FindByAvatar(img.ID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
usages := make([]MediaUsage, 0, len(found))
|
|
for _, a := range found {
|
|
usages = append(usages, MediaUsage{Type: UsageTypeAuthor, ID: a.ID, Title: a.Name, Fields: []string{UsageFieldAvatar}})
|
|
}
|
|
return usages, nil
|
|
}
|
|
|
|
func (u *GetMediaUsageUseCase) siteUsages(img image.Image) ([]MediaUsage, error) {
|
|
all, err := u.sites.List()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
usages := []MediaUsage{}
|
|
for _, s := range all {
|
|
if s.DefaultSocialImageID != nil && *s.DefaultSocialImageID == img.ID {
|
|
usages = append(usages, MediaUsage{Type: UsageTypeSite, ID: s.ID, Title: s.Name, Fields: []string{UsageFieldDefaultSocialImage}})
|
|
}
|
|
}
|
|
slices.SortFunc(usages, func(a, b MediaUsage) int { return int(a.ID) - int(b.ID) })
|
|
return usages, nil
|
|
}
|
|
|
|
func articleMentionsImage(a article.Article, img image.Image) bool {
|
|
return image.ContainsStoredName(a.Body, image.StoredName(img.Path)) || a.UsesImage(img.ID)
|
|
}
|