Files
RenewCMS/internal/infrastructure/useCases/DescribeArticle.go
T

105 lines
3.6 KiB
Go

package useCases
import (
"RenewCMS/internal/domain/article"
"RenewCMS/internal/domain/builtintype"
"RenewCMS/internal/domain/language"
"RenewCMS/internal/domain/localization"
"RenewCMS/internal/domain/role"
"errors"
"slices"
)
type DescribeArticleUseCase struct {
articles article.Repository
localization localization.Repository
}
type ArticleContext struct {
Capabilities []string
Translations []article.Translation
Source *article.Source
SourceUnavailable bool
MissingLangs []string
ExcludedFromPublic bool
PartialView bool
}
func NewDescribeArticleUseCase(articles article.Repository, localizationRepository localization.Repository) *DescribeArticleUseCase {
return &DescribeArticleUseCase{articles: articles, localization: localizationRepository}
}
func (d *DescribeArticleUseCase) Describe(art article.Article, caller ContentCaller) (ArticleContext, error) {
rows, err := d.localization.Rows(art.SiteID)
if err != nil {
return ArticleContext{}, err
}
described := ArticleContext{
Capabilities: caller.capabilitiesFor(art),
Translations: []article.Translation{},
MissingLangs: []string{},
ExcludedFromPublic: !localization.PublicTag(rows, art.Lang),
PartialView: caller.Grants.ScopeFor(role.KeyContentRead, builtintype.KeyArticle) != role.ScopeAll,
}
if caller.isAnonymous() {
return described, nil
}
readable, err := d.readableVersions(art, caller)
if err != nil {
return ArticleContext{}, err
}
described.Translations = translationsOf(caller, sortByLanguage(readable, rows), rows)
described.MissingLangs = missingLanguages(readable, rows, caller.Grants.HoldsFor(role.KeyContentCreate, builtintype.KeyArticle))
described.Source, described.SourceUnavailable, err = d.source(art, caller)
return described, err
}
func (d *DescribeArticleUseCase) source(art article.Article, caller ContentCaller) (*article.Source, bool, error) {
if art.SourceArticleID == nil {
return nil, false, nil
}
origin, err := d.articles.Get(*art.SourceArticleID)
if errors.Is(err, article.ErrArticleNotFound) || err == nil && !caller.seesArticle(origin) {
return nil, true, nil
}
if err != nil {
return nil, false, err
}
return &article.Source{ArticleID: origin.ID, Revision: art.SourceRevision}, false, nil
}
func (d *DescribeArticleUseCase) readableVersions(art article.Article, caller ContentCaller) ([]article.Article, error) {
if art.StoryID == 0 {
return []article.Article{art}, nil
}
versions, err := d.articles.ListByStory(art.StoryID)
if err != nil {
return nil, err
}
return caller.readableVersions(versions), nil
}
func translationsOf(caller ContentCaller, ordered []article.Article, rows []localization.Row) []article.Translation {
translations := make([]article.Translation, 0, len(ordered))
for _, version := range ordered {
translations = append(translations, article.Translation{
ID: version.ID,
Lang: version.Lang,
Title: version.Title,
IsOnline: version.IsOnline,
LanguageEnabled: version.Lang == language.Unassigned || localization.EnabledTag(rows, version.Lang),
ExcludedFromPublic: !localization.PublicTag(rows, version.Lang),
SourceArticleID: trackedSourceID(version, ordered),
Capabilities: caller.capabilitiesFor(version),
})
}
return translations
}
func trackedSourceID(version article.Article, readable []article.Article) *uint32 {
if version.SourceArticleID == nil || !slices.ContainsFunc(readable, func(candidate article.Article) bool { return candidate.ID == *version.SourceArticleID }) {
return nil
}
return version.SourceArticleID
}