diff --git a/internal/domain/config/entity/config.go b/internal/domain/config/entity/config.go index afc9579e..07a98476 100644 --- a/internal/domain/config/entity/config.go +++ b/internal/domain/config/entity/config.go @@ -25,7 +25,7 @@ type Config struct { MinifyC - Taxonomy + *Taxonomy } func (c *Config) Fs() afero.Fs { diff --git a/internal/domain/config/entity/taxonomies.go b/internal/domain/config/entity/taxonomies.go index 53a70e9f..f4ad372a 100644 --- a/internal/domain/config/entity/taxonomies.go +++ b/internal/domain/config/entity/taxonomies.go @@ -17,11 +17,11 @@ type Taxonomy struct { viewsByTreeKey map[string]valueobject.ViewName } -func (t Taxonomy) Views() []valueobject.ViewName { +func (t *Taxonomy) Views() []valueobject.ViewName { return t.views } -func (t Taxonomy) SetupViews() { +func (t *Taxonomy) SetupViews() { var views []valueobject.ViewName for k, v := range t.Taxonomies { views = append(views, valueobject.ViewName{Singular: k, Plural: v, PluralTreeKey: cleanTreeKey(v)}) diff --git a/internal/domain/config/factory/provider.go b/internal/domain/config/factory/provider.go index 6ccbe0e3..58bebf6f 100644 --- a/internal/domain/config/factory/provider.go +++ b/internal/domain/config/factory/provider.go @@ -60,7 +60,7 @@ func LoadConfig() (*entity.Config, error) { Imaging: entity.Imaging{}, MediaType: entity.MediaType{}, - Taxonomy: entity.Taxonomy{}, + Taxonomy: &entity.Taxonomy{}, } if err := l.assembleConfig(c); err != nil { diff --git a/internal/domain/content/entity/hugo.go b/internal/domain/content/entity/hugo.go index a89c6762..8f27d4d3 100644 --- a/internal/domain/content/entity/hugo.go +++ b/internal/domain/content/entity/hugo.go @@ -77,6 +77,10 @@ func (h *Hugo) previewPath(fullPath string) (string, error) { return "", fmt.Errorf("get relative path error: %v", err) } + if !strings.HasPrefix(relPath, "/") { + relPath = "/" + relPath + } + return relPath, nil } diff --git a/internal/domain/contenthub/entity/pagebuilder.go b/internal/domain/contenthub/entity/pagebuilder.go index d86c9e91..67089b77 100644 --- a/internal/domain/contenthub/entity/pagebuilder.go +++ b/internal/domain/contenthub/entity/pagebuilder.go @@ -284,7 +284,7 @@ func (b *PageBuilder) parseKind() error { v := b.Taxonomy.getTaxonomy(path.Path()) if !b.Taxonomy.IsZero(v) { // Either a taxonomy or a term. - if b.Taxonomy.PluralTreeKey(path.Path()) == path.Path() { + if b.Taxonomy.IsTaxonomyPath(path.Path()) { kind = valueobject.KindTaxonomy } else { kind = valueobject.KindTerm diff --git a/internal/domain/contenthub/entity/pagemap.go b/internal/domain/contenthub/entity/pagemap.go index dd63c693..f6c4fed5 100644 --- a/internal/domain/contenthub/entity/pagemap.go +++ b/internal/domain/contenthub/entity/pagemap.go @@ -257,7 +257,7 @@ func (m *PageMap) getPagesInSection(langIndex int, q pageMapQueryPagesInSection) w.Handle = func(key string, n *PageTreesNode, match doctree.DimensionFlag) (bool, error) { if q.Recursive { p, found := n.getPage() - if found && include(p.(*Page)) { + if found && include(p) { pas = append(pas, p) } @@ -265,7 +265,7 @@ func (m *PageMap) getPagesInSection(langIndex int, q pageMapQueryPagesInSection) } p, found := n.getPage() - if found && include(p.(*Page)) { + if found && include(p) { pas = append(pas, p) } @@ -285,7 +285,7 @@ func (m *PageMap) getPagesInSection(langIndex int, q pageMapQueryPagesInSection) if q.IncludeSelf { if n := tree.Get(q.Path); n != nil { p, found := n.getPage() - if found && include(p.(*Page)) { + if found && include(p) { pas = append(pas, p) } } @@ -327,7 +327,7 @@ func (m *PageMap) getPagesWithTerm(q pageMapQueryPagesBelowPath) contenthub.Page doctree.LockTypeNone, paths.AddTrailingSlash(q.Path), func(s string, n *WeightedTermTreeNode) (bool, error) { - if include(n.term.Page.(*Page)) { + if include(n.term.Page) { pas = append(pas, n.term) } diff --git a/internal/domain/contenthub/entity/pagemeta.go b/internal/domain/contenthub/entity/pagemeta.go index b5d76670..877aba49 100644 --- a/internal/domain/contenthub/entity/pagemeta.go +++ b/internal/domain/contenthub/entity/pagemeta.go @@ -27,6 +27,10 @@ func (m *Meta) PageWeight() int { return m.Weight } +func (m *Meta) ShouldList(global bool) bool { + return m.shouldList(global) +} + func (m *Meta) shouldList(global bool) bool { switch m.List { case Always: @@ -39,10 +43,18 @@ func (m *Meta) shouldList(global bool) bool { return false } +func (m *Meta) ShouldListAny() bool { + return m.shouldListAny() +} + func (m *Meta) shouldListAny() bool { return m.shouldList(true) || m.shouldList(false) } +func (m *Meta) NoLink() bool { + return m.noLink() +} + func (m *Meta) noLink() bool { return false // TODO, updated based on configuration } diff --git a/internal/domain/contenthub/entity/pagequery.go b/internal/domain/contenthub/entity/pagequery.go index ec777d8f..fe8b73e5 100644 --- a/internal/domain/contenthub/entity/pagequery.go +++ b/internal/domain/contenthub/entity/pagequery.go @@ -8,38 +8,38 @@ import ( ) var pagePredicates = struct { - KindPage predicate.P[*Page] - KindSection predicate.P[*Page] - KindHome predicate.P[*Page] - KindTerm predicate.P[*Page] - ShouldListLocal predicate.P[*Page] - ShouldListGlobal predicate.P[*Page] - ShouldListAny predicate.P[*Page] + KindPage predicate.P[contenthub.Page] + KindSection predicate.P[contenthub.Page] + KindHome predicate.P[contenthub.Page] + KindTerm predicate.P[contenthub.Page] + ShouldListLocal predicate.P[contenthub.Page] + ShouldListGlobal predicate.P[contenthub.Page] + ShouldListAny predicate.P[contenthub.Page] ShouldLink predicate.P[contenthub.Page] }{ - KindPage: func(p *Page) bool { + KindPage: func(p contenthub.Page) bool { return p.Kind() == valueobject.KindPage }, - KindSection: func(p *Page) bool { + KindSection: func(p contenthub.Page) bool { return p.Kind() == valueobject.KindSection }, - KindHome: func(p *Page) bool { + KindHome: func(p contenthub.Page) bool { return p.Kind() == valueobject.KindHome }, - KindTerm: func(p *Page) bool { + KindTerm: func(p contenthub.Page) bool { return p.Kind() == valueobject.KindTerm }, - ShouldListLocal: func(p *Page) bool { - return p.Meta.shouldList(false) + ShouldListLocal: func(p contenthub.Page) bool { + return p.ShouldList(false) }, - ShouldListGlobal: func(p *Page) bool { - return p.Meta.shouldList(true) + ShouldListGlobal: func(p contenthub.Page) bool { + return p.ShouldList(true) }, - ShouldListAny: func(p *Page) bool { - return p.Meta.shouldListAny() + ShouldListAny: func(p contenthub.Page) bool { + return p.ShouldListAny() }, ShouldLink: func(p contenthub.Page) bool { - return !p.(*Page).Meta.noLink() + return !p.NoLink() }, } @@ -65,7 +65,7 @@ type pageMapQueryPagesBelowPath struct { // Page inclusion filter. // May be nil. - Include predicate.P[*Page] + Include predicate.P[contenthub.Page] } func (q pageMapQueryPagesBelowPath) Key() string { diff --git a/internal/domain/contenthub/entity/taxonomy.go b/internal/domain/contenthub/entity/taxonomy.go index 7163a73d..86b9ada6 100644 --- a/internal/domain/contenthub/entity/taxonomy.go +++ b/internal/domain/contenthub/entity/taxonomy.go @@ -43,6 +43,16 @@ func (t *Taxonomy) Assemble(pages *doctree.NodeShiftTree[*PageTreesNode], pb *Pa return nil } +func (t *Taxonomy) IsTaxonomyPath(p string) bool { + ta := t.getTaxonomy(p) + + if ta == nil { + return false + } + + return p == path.Join(t.PluralTreeKey(ta.Plural()), "_index.md") +} + func (t *Taxonomy) PluralTreeKey(plural string) string { return cleanTreeKey(plural) } diff --git a/internal/domain/contenthub/type.go b/internal/domain/contenthub/type.go index 3c65f9e9..a95dbc57 100644 --- a/internal/domain/contenthub/type.go +++ b/internal/domain/contenthub/type.go @@ -258,6 +258,10 @@ type PageMeta interface { Description() string Params() maps.Params PageWeight() int + + ShouldList(global bool) bool + ShouldListAny() bool + NoLink() bool } type PageOutput interface { diff --git a/internal/domain/contenthub/valueobject/pagenop.go b/internal/domain/contenthub/valueobject/pagenop.go index c05c60e5..02fd78b4 100644 --- a/internal/domain/contenthub/valueobject/pagenop.go +++ b/internal/domain/contenthub/valueobject/pagenop.go @@ -14,6 +14,21 @@ var ( // PageNop implements Page, but does nothing. type nopPage int +func (p *nopPage) ShouldList(global bool) bool { + //TODO implement me + panic("implement me") +} + +func (p *nopPage) ShouldListAny() bool { + //TODO implement me + panic("implement me") +} + +func (p *nopPage) NoLink() bool { + //TODO implement me + panic("implement me") +} + func (p *nopPage) PageWeight() int { //TODO implement me panic("implement me") diff --git a/internal/domain/site/entity/navigation.go b/internal/domain/site/entity/navigation.go index e0bde773..ba744a81 100644 --- a/internal/domain/site/entity/navigation.go +++ b/internal/domain/site/entity/navigation.go @@ -3,8 +3,15 @@ package entity import ( "github.com/gohugonet/hugoverse/internal/domain/contenthub" "github.com/gohugonet/hugoverse/internal/domain/site/valueobject" + "github.com/gohugonet/hugoverse/pkg/compare" + "sort" ) +type Navigation struct { + taxonomies TaxonomyList + menus valueobject.Menus +} + // The TaxonomyList is a list of all taxonomies and their values // e.g. List['tags'] => TagTaxonomy (from above) type TaxonomyList map[string]Taxonomy @@ -14,9 +21,99 @@ type TaxonomyList map[string]Taxonomy // // TagTaxonomy['technology'] = WeightedPages // TagTaxonomy['go'] = WeightedPages -type Taxonomy map[string]contenthub.OrdinalWeightPage +type Taxonomy map[string]WeightedPages -type Navigation struct { - taxonomies TaxonomyList - menus valueobject.Menus +// OrderedTaxonomy is another representation of an Taxonomy using an array rather than a map. +// Important because you can't order a map. +type OrderedTaxonomy []OrderedTaxonomyEntry + +// getOneOPage returns one page in the taxonomy, +// nil if there is none. +func (t OrderedTaxonomy) getOneOPage() contenthub.Page { + if len(t) == 0 { + return nil + } + return t[0].WeightedPages.Page() +} + +// WeightedPages is a list of Pages with their corresponding (and relative) weight +// [{Weight: 30, Page: *1}, {Weight: 40, Page: *2}] +type WeightedPages []contenthub.OrdinalWeightPage + +// Page will return the Page (of Kind taxonomyList) that represents this set +// of pages. This method will panic if p is empty, as that should never happen. +func (p WeightedPages) Page() contenthub.Page { + if len(p) == 0 { + panic("WeightedPages is empty") + } + + return p[0] +} + +// OrderedTaxonomyEntry is similar to an element of a Taxonomy, but with the key embedded (as name) +// e.g: {Name: Technology, WeightedPages: TaxonomyPages} +type OrderedTaxonomyEntry struct { + Name string + WeightedPages +} + +// ByCount returns an ordered taxonomy sorted by # of pages per key. +// If taxonomies have the same # of pages, sort them alphabetical +func (i Taxonomy) ByCount() OrderedTaxonomy { + count := func(i1, i2 *OrderedTaxonomyEntry) bool { + li1 := len(i1.WeightedPages) + li2 := len(i2.WeightedPages) + + if li1 == li2 { + return compare.LessStrings(i1.Name, i2.Name) + } + return li1 > li2 + } + + ia := i.TaxonomyArray() + oiBy(count).Sort(ia) + return ia +} + +// TaxonomyArray returns an ordered taxonomy with a non defined order. +func (i Taxonomy) TaxonomyArray() OrderedTaxonomy { + ies := make([]OrderedTaxonomyEntry, len(i)) + count := 0 + for k, v := range i { + ies[count] = OrderedTaxonomyEntry{Name: k, WeightedPages: v} + count++ + } + return ies +} + +// Closure used in the Sort.Less method. +type oiBy func(i1, i2 *OrderedTaxonomyEntry) bool + +func (by oiBy) Sort(taxonomy OrderedTaxonomy) { + ps := &orderedTaxonomySorter{ + taxonomy: taxonomy, + by: by, // The Sort method's receiver is the function (closure) that defines the sort order. + } + sort.Stable(ps) +} + +// A type to implement the sort interface for TaxonomyEntries. +type orderedTaxonomySorter struct { + taxonomy OrderedTaxonomy + by oiBy +} + +// Len is part of sort.Interface. +func (s *orderedTaxonomySorter) Len() int { + return len(s.taxonomy) +} + +// Swap is part of sort.Interface. +func (s *orderedTaxonomySorter) Swap(i, j int) { + s.taxonomy[i], s.taxonomy[j] = s.taxonomy[j], s.taxonomy[i] +} + +// Less is part of sort.Interface. It is implemented by calling the "by" closure in the sorter. +func (s *orderedTaxonomySorter) Less(i, j int) bool { + return s.by(&s.taxonomy[i], &s.taxonomy[j]) } diff --git a/internal/domain/site/entity/siteinit.go b/internal/domain/site/entity/siteinit.go index b53bb408..adaba343 100644 --- a/internal/domain/site/entity/siteinit.go +++ b/internal/domain/site/entity/siteinit.go @@ -64,7 +64,12 @@ func (s *Site) PrepareLazyLoads() { tax = make(Taxonomy) s.Navigation.taxonomies[taxonomy] = tax } - tax[term] = page + weightedPages := tax[term] + if weightedPages == nil { + weightedPages = WeightedPages{page} + tax[term] = weightedPages + } + tax[term] = append(weightedPages, page) return nil }); err != nil { diff --git a/internal/domain/site/entity/url.go b/internal/domain/site/entity/url.go index 1f09cb11..c380f99a 100644 --- a/internal/domain/site/entity/url.go +++ b/internal/domain/site/entity/url.go @@ -91,7 +91,8 @@ func (s *Site) RelURL(in string) string { u = s.URL.addContextRoot(u) u = s.URL.handleRootSuffix(in, u) - u = s.URL.handlePrefix(u) + + //u = s.URL.handlePrefix(u) // our use case include preview, just put it relative to the html file return u }