feat: Google News sitemap
- sitemap-news.xml lists the articles (content kind article) first published in the last two days, newest first, at most 1000, with the publication name (news_publication, else app.name), the language of the locale, the publication date and the title - news_sources narrows it to type:<key> and category:<key>:<id> values; the Images and News tab lists the values to use in a partial - the index lists the file while news_enabled is on; otherwise it is 404
This commit is contained in:
@@ -92,7 +92,7 @@ func (Settings) TableName() string { return "golem15_sitemap_settings" }
|
||||
|
||||
// Fillable lists the columns the settings form edits.
|
||||
func (Settings) Fillable() []string {
|
||||
return []string{"site_url", "file_size", "extra_urls"}
|
||||
return []string{"site_url", "file_size", "extra_urls", "images_enabled", "news_enabled", "news_publication", "news_sources"}
|
||||
}
|
||||
|
||||
// Rules is empty: the settings controller runs Validate before a save.
|
||||
@@ -103,8 +103,13 @@ const (
|
||||
errFileSize = "golem15.sitemap::lang.errors.file_size"
|
||||
errURLScheme = "golem15.sitemap::lang.errors.url_scheme"
|
||||
errLastmod = "golem15.sitemap::lang.errors.lastmod"
|
||||
errNewsName = "golem15.sitemap::lang.errors.news_publication"
|
||||
errNewsSrc = "golem15.sitemap::lang.errors.news_source"
|
||||
)
|
||||
|
||||
// MaxNewsPublication is the longest publication name.
|
||||
const MaxNewsPublication = 255
|
||||
|
||||
// Validate answers every problem Problems finds as a validation error on
|
||||
// its field; the settings controller calls it before a save, so the save
|
||||
// answers 422 and the row is left as it was.
|
||||
@@ -140,9 +145,52 @@ func (s Settings) Problems() map[string][]string {
|
||||
out["extra_urls."+strconv.Itoa(i)+".lastmod"] = []string{errLastmod}
|
||||
}
|
||||
}
|
||||
if name := Text(s.NewsPublication); utf8.RuneCountInString(name) > MaxNewsPublication || strings.ContainsFunc(name, unicode.IsControl) {
|
||||
out["news_publication"] = []string{errNewsName}
|
||||
}
|
||||
for _, v := range s.NewsSources.Get() {
|
||||
if !ValidNewsSource(strings.TrimSpace(v)) {
|
||||
out["news_sources"] = []string{errNewsSrc}
|
||||
break
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// The prefixes of the news_sources values (P33-15): type:<type key> and
|
||||
// category:<type key>:<id>.
|
||||
const (
|
||||
NewsSourceType = "type:"
|
||||
NewsSourceCategory = "category:"
|
||||
)
|
||||
|
||||
// ValidNewsSource reports whether v is a news_sources value:
|
||||
// type:<type key> or category:<type key>:<id>, each part 1 to 128 letters,
|
||||
// digits, '-', '_' or '.'.
|
||||
func ValidNewsSource(v string) bool {
|
||||
switch {
|
||||
case strings.HasPrefix(v, NewsSourceType):
|
||||
return validSourcePart(strings.TrimPrefix(v, NewsSourceType))
|
||||
case strings.HasPrefix(v, NewsSourceCategory):
|
||||
rest := strings.TrimPrefix(v, NewsSourceCategory)
|
||||
i := strings.LastIndex(rest, ":")
|
||||
return i > 0 && validSourcePart(rest[:i]) && validSourcePart(rest[i+1:])
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func validSourcePart(s string) bool {
|
||||
if s == "" || len(s) > 128 {
|
||||
return false
|
||||
}
|
||||
for _, r := range s {
|
||||
if !(r >= 'a' && r <= 'z' || r >= 'A' && r <= 'Z' || r >= '0' && r <= '9' || r == '-' || r == '_' || r == '.') {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// AllowedFrontendURL reports whether u may be the site address: the empty
|
||||
// string, or an absolute http or https URL with a host and without
|
||||
// whitespace, control characters, a double quote, <, > or &. The value
|
||||
|
||||
@@ -48,3 +48,54 @@ fields:
|
||||
comment: golem15.sitemap::lang.settings.extra_url_lastmod_comment
|
||||
type: text
|
||||
span: right
|
||||
|
||||
images_enabled:
|
||||
label: golem15.sitemap::lang.settings.images_enabled
|
||||
comment: golem15.sitemap::lang.settings.images_enabled_comment
|
||||
type: switch
|
||||
default: true
|
||||
span: full
|
||||
tab: golem15.sitemap::lang.settings.tab_media
|
||||
|
||||
news_enabled:
|
||||
label: golem15.sitemap::lang.settings.news_enabled
|
||||
comment: golem15.sitemap::lang.settings.news_enabled_comment
|
||||
type: switch
|
||||
default: false
|
||||
span: full
|
||||
tab: golem15.sitemap::lang.settings.tab_media
|
||||
|
||||
news_publication:
|
||||
label: golem15.sitemap::lang.settings.news_publication
|
||||
comment: golem15.sitemap::lang.settings.news_publication_comment
|
||||
type: text
|
||||
span: left
|
||||
tab: golem15.sitemap::lang.settings.tab_media
|
||||
trigger:
|
||||
action: hide
|
||||
field: news_enabled
|
||||
condition: unchecked
|
||||
|
||||
# Taglist options are a fixed list, so the article types and their
|
||||
# categories are listed by the news_sources partial below instead.
|
||||
news_sources:
|
||||
label: golem15.sitemap::lang.settings.news_sources
|
||||
comment: golem15.sitemap::lang.settings.news_sources_comment
|
||||
type: taglist
|
||||
mode: array
|
||||
span: right
|
||||
tab: golem15.sitemap::lang.settings.tab_media
|
||||
trigger:
|
||||
action: hide
|
||||
field: news_enabled
|
||||
condition: unchecked
|
||||
|
||||
_news_sources:
|
||||
type: partial
|
||||
path: news_sources
|
||||
span: full
|
||||
tab: golem15.sitemap::lang.settings.tab_media
|
||||
trigger:
|
||||
action: hide
|
||||
field: news_enabled
|
||||
condition: unchecked
|
||||
|
||||
Reference in New Issue
Block a user