github.com/elmarschill/hugo_sample@v0.47.1/hugolib/pageGroup.go (about) 1 // Copyright 2015 The Hugo Authors. All rights reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // http://www.apache.org/licenses/LICENSE-2.0 7 // 8 // Unless required by applicable law or agreed to in writing, software 9 // distributed under the License is distributed on an "AS IS" BASIS, 10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 package hugolib 15 16 import ( 17 "errors" 18 "reflect" 19 "sort" 20 "strings" 21 "time" 22 ) 23 24 // PageGroup represents a group of pages, grouped by the key. 25 // The key is typically a year or similar. 26 type PageGroup struct { 27 Key interface{} 28 Pages 29 } 30 31 type mapKeyValues []reflect.Value 32 33 func (v mapKeyValues) Len() int { return len(v) } 34 func (v mapKeyValues) Swap(i, j int) { v[i], v[j] = v[j], v[i] } 35 36 type mapKeyByInt struct{ mapKeyValues } 37 38 func (s mapKeyByInt) Less(i, j int) bool { return s.mapKeyValues[i].Int() < s.mapKeyValues[j].Int() } 39 40 type mapKeyByStr struct{ mapKeyValues } 41 42 func (s mapKeyByStr) Less(i, j int) bool { 43 return s.mapKeyValues[i].String() < s.mapKeyValues[j].String() 44 } 45 46 func sortKeys(v []reflect.Value, order string) []reflect.Value { 47 if len(v) <= 1 { 48 return v 49 } 50 51 switch v[0].Kind() { 52 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 53 if order == "desc" { 54 sort.Sort(sort.Reverse(mapKeyByInt{v})) 55 } else { 56 sort.Sort(mapKeyByInt{v}) 57 } 58 case reflect.String: 59 if order == "desc" { 60 sort.Sort(sort.Reverse(mapKeyByStr{v})) 61 } else { 62 sort.Sort(mapKeyByStr{v}) 63 } 64 } 65 return v 66 } 67 68 // PagesGroup represents a list of page groups. 69 // This is what you get when doing page grouping in the templates. 70 type PagesGroup []PageGroup 71 72 // Reverse reverses the order of this list of page groups. 73 func (p PagesGroup) Reverse() PagesGroup { 74 for i, j := 0, len(p)-1; i < j; i, j = i+1, j-1 { 75 p[i], p[j] = p[j], p[i] 76 } 77 78 return p 79 } 80 81 var ( 82 errorType = reflect.TypeOf((*error)(nil)).Elem() 83 pagePtrType = reflect.TypeOf((*Page)(nil)) 84 ) 85 86 // GroupBy groups by the value in the given field or method name and with the given order. 87 // Valid values for order is asc, desc, rev and reverse. 88 func (p Pages) GroupBy(key string, order ...string) (PagesGroup, error) { 89 if len(p) < 1 { 90 return nil, nil 91 } 92 93 direction := "asc" 94 95 if len(order) > 0 && (strings.ToLower(order[0]) == "desc" || strings.ToLower(order[0]) == "rev" || strings.ToLower(order[0]) == "reverse") { 96 direction = "desc" 97 } 98 99 var ft interface{} 100 m, ok := pagePtrType.MethodByName(key) 101 if ok { 102 if m.Type.NumIn() != 1 || m.Type.NumOut() == 0 || m.Type.NumOut() > 2 { 103 return nil, errors.New(key + " is a Page method but you can't use it with GroupBy") 104 } 105 if m.Type.NumOut() == 1 && m.Type.Out(0).Implements(errorType) { 106 return nil, errors.New(key + " is a Page method but you can't use it with GroupBy") 107 } 108 if m.Type.NumOut() == 2 && !m.Type.Out(1).Implements(errorType) { 109 return nil, errors.New(key + " is a Page method but you can't use it with GroupBy") 110 } 111 ft = m 112 } else { 113 ft, ok = pagePtrType.Elem().FieldByName(key) 114 if !ok { 115 return nil, errors.New(key + " is neither a field nor a method of Page") 116 } 117 } 118 119 var tmp reflect.Value 120 switch e := ft.(type) { 121 case reflect.StructField: 122 tmp = reflect.MakeMap(reflect.MapOf(e.Type, reflect.SliceOf(pagePtrType))) 123 case reflect.Method: 124 tmp = reflect.MakeMap(reflect.MapOf(e.Type.Out(0), reflect.SliceOf(pagePtrType))) 125 } 126 127 for _, e := range p { 128 ppv := reflect.ValueOf(e) 129 var fv reflect.Value 130 switch ft.(type) { 131 case reflect.StructField: 132 fv = ppv.Elem().FieldByName(key) 133 case reflect.Method: 134 fv = ppv.MethodByName(key).Call([]reflect.Value{})[0] 135 } 136 if !fv.IsValid() { 137 continue 138 } 139 if !tmp.MapIndex(fv).IsValid() { 140 tmp.SetMapIndex(fv, reflect.MakeSlice(reflect.SliceOf(pagePtrType), 0, 0)) 141 } 142 tmp.SetMapIndex(fv, reflect.Append(tmp.MapIndex(fv), ppv)) 143 } 144 145 sortedKeys := sortKeys(tmp.MapKeys(), direction) 146 r := make([]PageGroup, len(sortedKeys)) 147 for i, k := range sortedKeys { 148 r[i] = PageGroup{Key: k.Interface(), Pages: tmp.MapIndex(k).Interface().([]*Page)} 149 } 150 151 return r, nil 152 } 153 154 // GroupByParam groups by the given page parameter key's value and with the given order. 155 // Valid values for order is asc, desc, rev and reverse. 156 func (p Pages) GroupByParam(key string, order ...string) (PagesGroup, error) { 157 if len(p) < 1 { 158 return nil, nil 159 } 160 161 direction := "asc" 162 163 if len(order) > 0 && (strings.ToLower(order[0]) == "desc" || strings.ToLower(order[0]) == "rev" || strings.ToLower(order[0]) == "reverse") { 164 direction = "desc" 165 } 166 167 var tmp reflect.Value 168 var keyt reflect.Type 169 for _, e := range p { 170 param := e.getParamToLower(key) 171 if param != nil { 172 if _, ok := param.([]string); !ok { 173 keyt = reflect.TypeOf(param) 174 tmp = reflect.MakeMap(reflect.MapOf(keyt, reflect.SliceOf(pagePtrType))) 175 break 176 } 177 } 178 } 179 if !tmp.IsValid() { 180 return nil, errors.New("There is no such a param") 181 } 182 183 for _, e := range p { 184 param := e.getParam(key, false) 185 if param == nil || reflect.TypeOf(param) != keyt { 186 continue 187 } 188 v := reflect.ValueOf(param) 189 if !tmp.MapIndex(v).IsValid() { 190 tmp.SetMapIndex(v, reflect.MakeSlice(reflect.SliceOf(pagePtrType), 0, 0)) 191 } 192 tmp.SetMapIndex(v, reflect.Append(tmp.MapIndex(v), reflect.ValueOf(e))) 193 } 194 195 var r []PageGroup 196 for _, k := range sortKeys(tmp.MapKeys(), direction) { 197 r = append(r, PageGroup{Key: k.Interface(), Pages: tmp.MapIndex(k).Interface().([]*Page)}) 198 } 199 200 return r, nil 201 } 202 203 func (p Pages) groupByDateField(sorter func(p Pages) Pages, formatter func(p *Page) string, order ...string) (PagesGroup, error) { 204 if len(p) < 1 { 205 return nil, nil 206 } 207 208 sp := sorter(p) 209 210 if !(len(order) > 0 && (strings.ToLower(order[0]) == "asc" || strings.ToLower(order[0]) == "rev" || strings.ToLower(order[0]) == "reverse")) { 211 sp = sp.Reverse() 212 } 213 214 date := formatter(sp[0]) 215 var r []PageGroup 216 r = append(r, PageGroup{Key: date, Pages: make(Pages, 0)}) 217 r[0].Pages = append(r[0].Pages, sp[0]) 218 219 i := 0 220 for _, e := range sp[1:] { 221 date = formatter(e) 222 if r[i].Key.(string) != date { 223 r = append(r, PageGroup{Key: date}) 224 i++ 225 } 226 r[i].Pages = append(r[i].Pages, e) 227 } 228 return r, nil 229 } 230 231 // GroupByDate groups by the given page's Date value in 232 // the given format and with the given order. 233 // Valid values for order is asc, desc, rev and reverse. 234 // For valid format strings, see https://golang.org/pkg/time/#Time.Format 235 func (p Pages) GroupByDate(format string, order ...string) (PagesGroup, error) { 236 sorter := func(p Pages) Pages { 237 return p.ByDate() 238 } 239 formatter := func(p *Page) string { 240 return p.Date.Format(format) 241 } 242 return p.groupByDateField(sorter, formatter, order...) 243 } 244 245 // GroupByPublishDate groups by the given page's PublishDate value in 246 // the given format and with the given order. 247 // Valid values for order is asc, desc, rev and reverse. 248 // For valid format strings, see https://golang.org/pkg/time/#Time.Format 249 func (p Pages) GroupByPublishDate(format string, order ...string) (PagesGroup, error) { 250 sorter := func(p Pages) Pages { 251 return p.ByPublishDate() 252 } 253 formatter := func(p *Page) string { 254 return p.PublishDate.Format(format) 255 } 256 return p.groupByDateField(sorter, formatter, order...) 257 } 258 259 // GroupByExpiryDate groups by the given page's ExpireDate value in 260 // the given format and with the given order. 261 // Valid values for order is asc, desc, rev and reverse. 262 // For valid format strings, see https://golang.org/pkg/time/#Time.Format 263 func (p Pages) GroupByExpiryDate(format string, order ...string) (PagesGroup, error) { 264 sorter := func(p Pages) Pages { 265 return p.ByExpiryDate() 266 } 267 formatter := func(p *Page) string { 268 return p.ExpiryDate.Format(format) 269 } 270 return p.groupByDateField(sorter, formatter, order...) 271 } 272 273 // GroupByParamDate groups by a date set as a param on the page in 274 // the given format and with the given order. 275 // Valid values for order is asc, desc, rev and reverse. 276 // For valid format strings, see https://golang.org/pkg/time/#Time.Format 277 func (p Pages) GroupByParamDate(key string, format string, order ...string) (PagesGroup, error) { 278 sorter := func(p Pages) Pages { 279 var r Pages 280 for _, e := range p { 281 param := e.getParamToLower(key) 282 if param != nil { 283 if _, ok := param.(time.Time); ok { 284 r = append(r, e) 285 } 286 } 287 } 288 pdate := func(p1, p2 *Page) bool { 289 return p1.getParamToLower(key).(time.Time).Unix() < p2.getParamToLower(key).(time.Time).Unix() 290 } 291 pageBy(pdate).Sort(r) 292 return r 293 } 294 formatter := func(p *Page) string { 295 return p.getParamToLower(key).(time.Time).Format(format) 296 } 297 return p.groupByDateField(sorter, formatter, order...) 298 }