package article import ( "RenewCMS/internal/domain/author" "errors" "slices" ) var ( ErrRevisionConflict = errors.New("the article changed since it was opened") ErrLanguageTaken = errors.New("the story already holds a version in that language") ErrLanguageUnavailable = errors.New("the language cannot be used for this story") ErrLanguageExists = errors.New("a readable version in that language already exists") ErrLanguageNotEnabled = errors.New("the language is not enabled on the site") ErrLanguageDisabled = errors.New("the language is disabled on the site") ErrSourceUnavailable = errors.New("the source version cannot be used") ErrSourceOtherStory = errors.New("the source version belongs to another story") ErrInvalidTranslation = errors.New("the translation request is not valid") ErrStoryMoveRequired = errors.New("the story must be moved as a whole") ErrSlugConflict = errors.New("an address already exists on the destination site") ErrTargetSiteDeleted = errors.New("the target site was deleted while the article was being created") ) const ( CapabilityRead = "read" CapabilityEdit = "edit" CapabilityPublish = "publish" CapabilityDelete = "delete" CapabilityCreateTranslation = "create_translation" ) type RevisionConflictError struct { Current uint32 } func (e *RevisionConflictError) Error() string { return ErrRevisionConflict.Error() } func (e *RevisionConflictError) Is(target error) bool { return target == ErrRevisionConflict } type LanguageExistsError struct { ArticleID uint32 } func (e *LanguageExistsError) Error() string { return ErrLanguageExists.Error() } func (e *LanguageExistsError) Is(target error) bool { return target == ErrLanguageExists } type LanguageNotEnabledError struct { Missing []string } func (e *LanguageNotEnabledError) Error() string { return ErrLanguageNotEnabled.Error() } func (e *LanguageNotEnabledError) Is(target error) bool { return target == ErrLanguageNotEnabled } type SlugConflictError struct { ArticleIDs []uint32 } func (e *SlugConflictError) Error() string { return ErrSlugConflict.Error() } func (e *SlugConflictError) Is(target error) bool { return target == ErrSlugConflict } type Translation struct { ID uint32 `json:"id"` Lang string `json:"lang"` Title string `json:"title"` IsOnline bool `json:"is_online"` LanguageEnabled bool `json:"language_enabled"` Capabilities []string `json:"capabilities"` } type Source struct { ArticleID uint32 `json:"article_id"` Revision uint32 `json:"revision"` } type Impact struct { Edited bool Content bool } func ImpactOf(before Article, after Article, authorsChanged bool) Impact { content := before.Title != after.Title || before.Body != after.Body || before.Excerpt != after.Excerpt || before.Slug != after.Slug || before.CoverImageAlt != after.CoverImageAlt || before.SEOTitle != after.SEOTitle || before.SEODescription != after.SEODescription || before.SocialTitle != after.SocialTitle || before.SocialDescription != after.SocialDescription edited := content || authorsChanged || before.SiteID != after.SiteID || before.CanonicalURL != after.CanonicalURL || before.Noindex != after.Noindex || !sameImageID(before.CoverImageID, after.CoverImageID) || !sameImageID(before.SocialImageID, after.SocialImageID) || !slices.Equal(before.SEODismissed, after.SEODismissed) return Impact{Edited: edited, Content: content} } const InitialRevision uint32 = 1 func (a Article) Opened() Article { a.EditRevision = InitialRevision a.ContentRevision = InitialRevision return a } func (a Article) Revised(impact Impact) Article { if impact.Edited { a.EditRevision++ } if impact.Content { a.ContentRevision++ } return a } func sameImageID(left *uint32, right *uint32) bool { if left == nil || right == nil { return left == right } return *left == *right } func (a Article) CopiedInto(draft Article) Article { draft.Body = a.Body draft.Excerpt = a.Excerpt draft.SEOTitle = a.SEOTitle draft.SEODescription = a.SEODescription draft.SocialTitle = a.SocialTitle draft.SocialDescription = a.SocialDescription draft.CoverImageID = a.CoverImageID draft.CoverImageAlt = a.CoverImageAlt draft.SocialImageID = a.SocialImageID draft.Authors = slices.Clone(a.Authors) if draft.Authors == nil { draft.Authors = []author.Byline{} } draft.SourceArticleID = &a.ID draft.SourceRevision = a.ContentRevision return draft }