forked from ikeikeikeike/go-sitemap-generator
/
builder_url.go
146 lines (128 loc) · 3.84 KB
/
builder_url.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
package stm
import (
"errors"
"fmt"
"time"
"github.com/beevik/etree"
"github.com/fatih/structs"
)
// URLModel is specific sample model for valuedate.
// http://www.sitemaps.org/protocol.html
// https://support.google.com/webmasters/answer/178636
type URLModel struct {
Priority float64 `valid:"float,length(0.0|1.0)"`
Changefreq string `valid:"alpha(always|hourly|daily|weekly|monthly|yearly|never)"`
Lastmod time.Time `valid:"-"`
Expires time.Time `valid:"-"`
Host string `valid:"ipv4"`
Loc string `valid:"url"`
Image string `valid:"url"`
Video string `valid:"url"`
Tag string `valid:""`
Geo string `valid:""`
News string `valid:"-"`
Mobile bool `valid:"-"`
Alternate string `valid:"-"`
Alternates map[string]interface{} `valid:"-"`
Pagemap map[string]interface{} `valid:"-"`
}
// fieldnames []string{"priority" "changefreq" "lastmod" "expires" "host" "images"
// "video" "geo" "news" "videos" "mobile" "alternate" "alternates" "pagemap"}
var fieldnames = ToLowerString(structs.Names(&URLModel{}))
// NewSitemapURL returns the created the SitemapURL's pointer
// and it validates URL types error.
func NewSitemapURL(opts *Options, url URL) (SitemapURL, error) {
smu := &sitemapURL{opts: opts, data: url}
err := smu.validate()
return smu, err
}
// sitemapURL provides xml validator and xml builder.
type sitemapURL struct {
opts *Options
data URL
}
// validate is checking correct keys and checks the existence.
// TODO: Will create value's validator
func (su *sitemapURL) validate() error {
var key string
var invalid bool
var locOk, hostOk bool
for _, value := range su.data {
key = value[0].(string)
switch key {
case "loc":
locOk = true
case "host":
hostOk = true
}
invalid = true
for _, name := range fieldnames {
if key == name {
invalid = false
break
}
}
if invalid {
break
}
}
if invalid {
msg := fmt.Sprintf("Unknown map's key `%s` in URL type", key)
return errors.New(msg)
}
if !locOk {
msg := fmt.Sprintf("URL type must have `loc` map's key")
return errors.New(msg)
}
if !hostOk {
msg := fmt.Sprintf("URL type must have `host` map's key")
return errors.New(msg)
}
return nil
}
// XML is building xml.
func (su *sitemapURL) XML() []byte {
doc := etree.NewDocument()
url := doc.CreateElement("url")
SetBuilderElementValue(url, su.data.URLJoinBy("loc", "host", "loc"), "loc")
if _, ok := SetBuilderElementValue(url, su.data, "lastmod"); !ok {
lastmod := url.CreateElement("lastmod")
lastmod.SetText(time.Now().Format(time.RFC3339))
}
if _, ok := SetBuilderElementValue(url, su.data, "changefreq"); !ok {
changefreq := url.CreateElement("changefreq")
changefreq.SetText("weekly")
}
if _, ok := SetBuilderElementValue(url, su.data, "priority"); !ok {
priority := url.CreateElement("priority")
priority.SetText("0.5")
}
// If there are any alternate URLs, prefix them with the host
host, _ := su.data.Get("host")
for _, pairs := range su.data {
if pairs[0] == "alternates" {
value := pairs[1]
if attrs, ok := value.([]Attr); ok {
for _, attr := range attrs {
attr["href"] = URLJoin(host.(string), attr["href"])
}
}
break
}
}
SetBuilderElementValue(url, su.data, "alternates")
SetBuilderElementValue(url, su.data, "expires")
SetBuilderElementValue(url, su.data, "mobile")
SetBuilderElementValue(url, su.data, "news")
SetBuilderElementValue(url, su.data, "video")
SetBuilderElementValue(url, su.data, "image")
SetBuilderElementValue(url, su.data, "geo")
if su.opts.pretty {
doc.Indent(2)
}
buf := poolBuffer.Get()
doc.WriteTo(buf)
bytes := buf.Bytes()
poolBuffer.Put(buf)
return bytes
}