- migration 202610280004: image_file_no, kind, published_at and parent_refs on the slot table; the image, News, IndexNow and HTML sitemap settings - the rebuild stores each page's featured and body images (absolute on the site, deduplicated, at most 1000) and numbers the rows with images into sitemap-images-<n>.xml with the per-type files' stable rules - the index lists the image files while images_enabled is on; the files route serves them with image:image and image:loc only
93 lines
3.7 KiB
Go
93 lines
3.7 KiB
Go
package models
|
|
|
|
import (
|
|
"strings"
|
|
"time"
|
|
"unicode"
|
|
"unicode/utf8"
|
|
|
|
"git.golem15.com/golem15/summercms/modules/lagoon"
|
|
)
|
|
|
|
// ExtraTypeKey is the type_key of the settings' additional addresses in
|
|
// golem15_sitemap_urls; their record_id is the hex SHA-256 of the address.
|
|
const ExtraTypeKey = "extra"
|
|
|
|
// ExtraGroup is the file group of the additional addresses.
|
|
const ExtraGroup = "pages"
|
|
|
|
// The excluded reasons the sitemap sets itself. A Filter's reasons
|
|
// (contract.ReasonNoindex and the others) are stored as it gives them.
|
|
const (
|
|
// ReasonDeleted is an address whose record or locale is gone.
|
|
ReasonDeleted = "deleted"
|
|
// ReasonUnpublished is an address whose record is not published.
|
|
ReasonUnpublished = "unpublished"
|
|
)
|
|
|
|
// Alternate is one hreflang alternate of an address: Hreflang the locale
|
|
// code or x-default, Href the alternate's path or absolute URL.
|
|
type Alternate struct {
|
|
Hreflang string `json:"hreflang"`
|
|
Href string `json:"href"`
|
|
}
|
|
|
|
// Ref points at a record: Type is its content type key and ID the record's
|
|
// ID within it (pact.ContentRef as stored in parent_refs).
|
|
type Ref struct {
|
|
Type string `json:"type"`
|
|
ID string `json:"id"`
|
|
}
|
|
|
|
// URL is one row of the slot table golem15_sitemap_urls (D-30): an address
|
|
// of a content type's record in one locale, or an additional address. Grp
|
|
// and FileNo place it in the file sitemap-<grp>-<file_no>.xml; FileNo is
|
|
// nil until the address is numbered. ExcludedReason is "" for a listed
|
|
// address; a row left out keeps its FileNo, so its file keeps a hole.
|
|
// Images are the page's image addresses (D-31), ImageFileNo the number of
|
|
// its sitemap-images file (nil while it has none). Kind is the content
|
|
// type's kind (pact.ContentKindArticle and the others), PublishedAt the
|
|
// record's first publication and ParentRefs its parents, for the News
|
|
// sitemap and the HTML sitemap.
|
|
type URL struct {
|
|
ID uint64 `gorm:"column:id;primaryKey"`
|
|
Grp string `gorm:"column:grp"`
|
|
TypeKey string `gorm:"column:type_key"`
|
|
RecordID string `gorm:"column:record_id"`
|
|
Locale string `gorm:"column:locale"`
|
|
URL string `gorm:"column:url"`
|
|
Title string `gorm:"column:title"`
|
|
FileNo *int `gorm:"column:file_no"`
|
|
FirstSeen time.Time `gorm:"column:first_seen"`
|
|
LastMod *time.Time `gorm:"column:lastmod"`
|
|
Alternates lagoon.Jsonable[[]Alternate] `gorm:"column:alternates"`
|
|
Images lagoon.Jsonable[[]string] `gorm:"column:images"`
|
|
ImageFileNo *int `gorm:"column:image_file_no"`
|
|
Kind string `gorm:"column:kind"`
|
|
PublishedAt *time.Time `gorm:"column:published_at"`
|
|
ParentRefs lagoon.Jsonable[[]Ref] `gorm:"column:parent_refs"`
|
|
ExcludedReason string `gorm:"column:excluded_reason"`
|
|
UpdatedAt time.Time `gorm:"column:updated_at"`
|
|
}
|
|
|
|
func (URL) TableName() string { return "golem15_sitemap_urls" }
|
|
|
|
func init() {
|
|
Register(URL{})
|
|
}
|
|
|
|
// AllowedAddress reports whether u may be a stored address: a path that
|
|
// starts with a single / (no whitespace, control characters, quotes, < or
|
|
// >), or an absolute http(s) URL that AllowedFrontendURL accepts.
|
|
func AllowedAddress(u string) bool {
|
|
if u == "" || !utf8.ValidString(u) {
|
|
return false
|
|
}
|
|
if strings.HasPrefix(u, "/") && !strings.HasPrefix(u, "//") {
|
|
return !strings.ContainsFunc(u, func(r rune) bool {
|
|
return unicode.IsControl(r) || unicode.IsSpace(r) || r == '"' || r == '<' || r == '>' || r == '\\'
|
|
})
|
|
}
|
|
return AllowedFrontendURL(u)
|
|
}
|