105 lines
3.6 KiB
Go
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
|
|
}
|