package useCases import ( "RenewCMS/internal/domain/article" "RenewCMS/internal/domain/language" "RenewCMS/internal/domain/localization" "RenewCMS/internal/domain/site" "errors" ) type ResolvePublicArticleUseCase struct { articles article.Repository sites site.Repository localization localization.Repository bodyRenderer article.BodyRenderer } func NewResolvePublicArticleUseCase(articles article.Repository, sites site.Repository, localizationRepository localization.Repository, bodyRenderer article.BodyRenderer) *ResolvePublicArticleUseCase { return &ResolvePublicArticleUseCase{articles: articles, sites: sites, localization: localizationRepository, bodyRenderer: bodyRenderer} } type PublicArticle struct { Article article.Article Viewer ContentCaller Context ArticleContext Alternates []article.Alternate RequestedLang string Fallback bool } type PublicStoryQuery struct { SiteKey string StoryID uint32 Lang *string FallbackDefault bool Format article.BodyFormat } type PublicLookupQuery struct { SiteKey string Lang string Slug string Format article.BodyFormat } func (u *ResolvePublicArticleUseCase) ByLangSlug(query PublicLookupQuery) (PublicArticle, error) { tag, err := language.Normalize(query.Lang) if errors.Is(err, language.ErrInvalidLanguage) { return PublicArticle{}, err } unsupported := errors.Is(err, language.ErrUnsupportedLanguage) owner, err := u.sites.GetByKey(query.SiteKey) if err != nil { return PublicArticle{}, err } if unsupported { return PublicArticle{}, article.ErrArticleNotFound } found, err := u.articles.GetByLangSlug(owner.ID, tag, query.Slug) if err != nil { return PublicArticle{}, err } if !found.IsOnline { return PublicArticle{}, article.ErrArticleNotFound } rows, err := u.localization.Rows(owner.ID) if err != nil { return PublicArticle{}, err } versions, err := storyVersions(u.articles, found) if err != nil { return PublicArticle{}, err } return u.present(owner, rows, versions, found, query.Format) } func (u *ResolvePublicArticleUseCase) ByStory(query PublicStoryQuery) (PublicArticle, error) { requested := "" unsupported := false if query.Lang != nil { tag, err := language.Normalize(*query.Lang) if errors.Is(err, language.ErrInvalidLanguage) { return PublicArticle{}, err } requested = tag unsupported = errors.Is(err, language.ErrUnsupportedLanguage) } owner, err := u.sites.GetByKey(query.SiteKey) if err != nil { return PublicArticle{}, err } rows, err := u.localization.Rows(owner.ID) if err != nil { return PublicArticle{}, err } if unsupported { return PublicArticle{}, article.ErrLanguageNotAvailable } defaultTag := localization.ConfirmedDefaultTag(rows) if query.Lang == nil { requested = defaultTag } versions, err := u.articles.ListByStory(query.StoryID) if err != nil { return PublicArticle{}, err } chosen, fallback, ok := chooseStoryVersion(owner, versions, requested, defaultTag, query.FallbackDefault) if !ok { return PublicArticle{}, article.ErrLanguageNotAvailable } presented, err := u.present(owner, rows, versions, chosen, query.Format) if err != nil { return PublicArticle{}, err } presented.RequestedLang = requested presented.Fallback = fallback return presented, nil } func chooseStoryVersion(owner site.Site, versions []article.Article, requested string, defaultTag string, fallbackDefault bool) (article.Article, bool, bool) { if requested == "" { return article.Article{}, false, false } if found, ok := publishedVersion(owner, versions, requested); ok { return found, false, true } if fallbackDefault && defaultTag != "" && defaultTag != requested { if found, ok := publishedVersion(owner, versions, defaultTag); ok { return found, true, true } } return article.Article{}, false, false } func publishedVersion(owner site.Site, versions []article.Article, tag string) (article.Article, bool) { for _, version := range versions { if version.SiteID == owner.ID && version.Lang == tag && version.IsOnline { return version, true } } return article.Article{}, false } func (u *ResolvePublicArticleUseCase) present(owner site.Site, rows []localization.Row, versions []article.Article, found article.Article, format article.BodyFormat) (PublicArticle, error) { rendered, err := renderArticle(u.bodyRenderer, found, format) if err != nil { return PublicArticle{}, err } viewer := ContentCaller{} described := ArticleContext{Capabilities: viewer.capabilitiesFor(found), Translations: []article.Translation{}} return PublicArticle{Article: rendered, Viewer: viewer, Context: described, Alternates: alternatesOf(owner, eligibleSiblings(rows, versions, found))}, nil }