contract.ListingSettings.TypesOff lists the signpost keys of the content types switched off in the sitemap settings, so another plugin (the SEO tab's sitemap status) can tell an address left out with ReasonType without reading the sitemap's tables. Additive.
153 lines
4.4 KiB
Go
153 lines
4.4 KiB
Go
package classes
|
|
|
|
import (
|
|
"context"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"git.golem15.com/golem15/sm-sitemap-plugin/contract"
|
|
"git.golem15.com/golem15/sm-sitemap-plugin/models"
|
|
"git.golem15.com/golem15/summercms/modules/backpack"
|
|
"git.golem15.com/golem15/summercms/modules/signpost"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
// Listing implements contract.Listing over the slot table and the
|
|
// settings (D-95): other plugins read the site address, the index address,
|
|
// the file groups and each group's listed addresses without importing the
|
|
// sitemap plugin.
|
|
type Listing struct {
|
|
App *backpack.App
|
|
}
|
|
|
|
var _ contract.Listing = Listing{}
|
|
|
|
// The page sizes of Entries.
|
|
const (
|
|
defaultEntriesLimit = 100
|
|
maxEntriesLimit = 1000
|
|
)
|
|
|
|
func (l Listing) db(ctx context.Context) (*gorm.DB, error) {
|
|
if l.App == nil {
|
|
return nil, ErrNoDatabase
|
|
}
|
|
db, ok := l.App.Lookup[*gorm.DB]()
|
|
if !ok || db == nil {
|
|
return nil, ErrNoDatabase
|
|
}
|
|
return db.WithContext(ctx), nil
|
|
}
|
|
|
|
// Settings answers the site address, the index address, the default
|
|
// locale and the groups with listed addresses (by key), with their titles
|
|
// (a content type's saved HTML sitemap headings, else the titles of the
|
|
// published Filter's group of that key), the HTML sitemap settings and the
|
|
// IndexNow key file name (while IndexNow is on with a key), and the
|
|
// content types switched off.
|
|
func (l Listing) Settings(ctx context.Context) (contract.ListingSettings, error) {
|
|
var out contract.ListingSettings
|
|
db, err := l.db(ctx)
|
|
if err != nil {
|
|
return out, err
|
|
}
|
|
settings, err := LoadSettings(db)
|
|
if err != nil {
|
|
return out, err
|
|
}
|
|
out.SiteURL = SiteURL(l.App, settings)
|
|
if out.SiteURL != "" {
|
|
out.IndexURL = out.SiteURL + "/sitemap.xml"
|
|
}
|
|
out.DefaultLocale = models.Text(settings.DefaultLocale)
|
|
var counts []struct {
|
|
Grp string `gorm:"column:grp"`
|
|
N int `gorm:"column:n"`
|
|
}
|
|
if err := db.Raw(`SELECT grp, COUNT(*) AS n FROM golem15_sitemap_urls WHERE excluded_reason = '' GROUP BY grp ORDER BY grp`).Scan(&counts).Error; err != nil {
|
|
return out, err
|
|
}
|
|
titles, _, _, err := groupTitles(ctx, l.App, db)
|
|
if err != nil {
|
|
return out, err
|
|
}
|
|
out.Groups = make([]contract.GroupInfo, 0, len(counts))
|
|
for _, c := range counts {
|
|
out.Groups = append(out.Groups, contract.GroupInfo{Key: c.Grp, Slug: c.Grp, Titles: titles[c.Grp], Count: c.N})
|
|
}
|
|
out.HTMLEnabled = settings.HTMLEnabled
|
|
out.HTMLPaths = settings.EffectiveHTMLPaths()
|
|
out.HTMLPageSize = settings.EffectiveHTMLPageSize()
|
|
out.KeyFile = KeyFileName(ActiveKey(settings))
|
|
out.TypesOff = []string{}
|
|
if reg, ok := l.App.Lookup[*signpost.Registry](); ok && reg != nil {
|
|
rows, err := LoadTypeSettings(db)
|
|
if err != nil {
|
|
return out, err
|
|
}
|
|
for _, typ := range reg.ContentTypes() {
|
|
if !EffectiveType(typ, rows).Include {
|
|
out.TypesOff = append(out.TypesOff, typ.Key)
|
|
}
|
|
}
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
// Entries pages a group's listed addresses in the order they were stored
|
|
// (row id); Next is the last row id of a page that has a successor. A
|
|
// Locale keeps the addresses of that locale and those without one.
|
|
func (l Listing) Entries(ctx context.Context, q contract.EntriesQuery) (contract.EntriesPage, error) {
|
|
page := contract.EntriesPage{Entries: []contract.Entry{}}
|
|
db, err := l.db(ctx)
|
|
if err != nil {
|
|
return page, err
|
|
}
|
|
settings, err := LoadSettings(db)
|
|
if err != nil {
|
|
return page, err
|
|
}
|
|
site := SiteURL(l.App, settings)
|
|
limit := q.Limit
|
|
if limit <= 0 {
|
|
limit = defaultEntriesLimit
|
|
}
|
|
limit = min(limit, maxEntriesLimit)
|
|
scope := func() *gorm.DB {
|
|
s := db.Model(&models.URL{}).Where("grp = ? AND excluded_reason = ''", q.Group)
|
|
if loc := strings.TrimSpace(q.Locale); loc != "" {
|
|
s = s.Where("locale = ? OR locale = ''", loc)
|
|
}
|
|
return s
|
|
}
|
|
var total int64
|
|
if err := scope().Count(&total).Error; err != nil {
|
|
return page, err
|
|
}
|
|
page.Total = int(total)
|
|
q2 := scope()
|
|
if after, err := strconv.ParseUint(strings.TrimSpace(q.After), 10, 64); err == nil && after > 0 {
|
|
q2 = q2.Where("id > ?", after)
|
|
}
|
|
var rows []models.URL
|
|
if err := q2.Order("id ASC").Limit(limit + 1).Find(&rows).Error; err != nil {
|
|
return page, err
|
|
}
|
|
if len(rows) > limit {
|
|
rows = rows[:limit]
|
|
page.Next = strconv.FormatUint(rows[len(rows)-1].ID, 10)
|
|
}
|
|
for _, r := range rows {
|
|
page.Entries = append(page.Entries, contract.Entry{
|
|
Group: r.Grp,
|
|
TypeKey: r.TypeKey,
|
|
RecordID: r.RecordID,
|
|
Locale: r.Locale,
|
|
URL: Absolute(site, r.URL),
|
|
Title: r.Title,
|
|
LastMod: r.LastMod,
|
|
})
|
|
}
|
|
return page, nil
|
|
}
|