github.com/graemephi/kahugo@v0.62.3-0.20211121071557-d78c0423784d/navigation/pagemenus.go (about) 1 // Copyright 2019 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 navigation 15 16 import ( 17 "github.com/gohugoio/hugo/common/maps" 18 "github.com/gohugoio/hugo/common/types" 19 20 "github.com/pkg/errors" 21 "github.com/spf13/cast" 22 ) 23 24 type PageMenusProvider interface { 25 PageMenusGetter 26 MenuQueryProvider 27 } 28 29 type PageMenusGetter interface { 30 Menus() PageMenus 31 } 32 33 type MenusGetter interface { 34 Menus() Menus 35 } 36 37 type MenuQueryProvider interface { 38 HasMenuCurrent(menuID string, me *MenuEntry) bool 39 IsMenuCurrent(menuID string, inme *MenuEntry) bool 40 } 41 42 func PageMenusFromPage(p Page) (PageMenus, error) { 43 params := p.Params() 44 45 ms, ok := params["menus"] 46 if !ok { 47 ms, ok = params["menu"] 48 } 49 50 pm := PageMenus{} 51 52 if !ok { 53 return nil, nil 54 } 55 56 me := MenuEntry{Page: p, Name: p.LinkTitle(), Weight: p.Weight()} 57 58 // Could be the name of the menu to attach it to 59 mname, err := cast.ToStringE(ms) 60 61 if err == nil { 62 me.Menu = mname 63 pm[mname] = &me 64 return pm, nil 65 } 66 67 // Could be a slice of strings 68 mnames, err := cast.ToStringSliceE(ms) 69 70 if err == nil { 71 for _, mname := range mnames { 72 me.Menu = mname 73 pm[mname] = &me 74 } 75 return pm, nil 76 } 77 78 var wrapErr = func(err error) error { 79 return errors.Wrapf(err, "unable to process menus for page %q", p.Path()) 80 } 81 82 // Could be a structured menu entry 83 menus, err := maps.ToStringMapE(ms) 84 if err != nil { 85 return pm, wrapErr(err) 86 } 87 88 for name, menu := range menus { 89 menuEntry := MenuEntry{Page: p, Name: p.LinkTitle(), Weight: p.Weight(), Menu: name} 90 if menu != nil { 91 ime, err := maps.ToStringMapE(menu) 92 if err != nil { 93 return pm, wrapErr(err) 94 } 95 96 if err = menuEntry.MarshallMap(ime); err != nil { 97 return pm, wrapErr(err) 98 } 99 } 100 pm[name] = &menuEntry 101 } 102 103 return pm, nil 104 } 105 106 func NewMenuQueryProvider( 107 pagem PageMenusGetter, 108 sitem MenusGetter, 109 p Page) MenuQueryProvider { 110 return &pageMenus{ 111 p: p, 112 pagem: pagem, 113 sitem: sitem, 114 } 115 } 116 117 type pageMenus struct { 118 pagem PageMenusGetter 119 sitem MenusGetter 120 p Page 121 } 122 123 func (pm *pageMenus) HasMenuCurrent(menuID string, me *MenuEntry) bool { 124 if !types.IsNil(me.Page) && me.Page.IsSection() { 125 if ok, _ := me.Page.IsAncestor(pm.p); ok { 126 return true 127 } 128 } 129 130 if !me.HasChildren() { 131 return false 132 } 133 134 menus := pm.pagem.Menus() 135 136 if m, ok := menus[menuID]; ok { 137 for _, child := range me.Children { 138 if child.IsEqual(m) { 139 return true 140 } 141 if pm.HasMenuCurrent(menuID, child) { 142 return true 143 } 144 } 145 } 146 147 if pm.p == nil { 148 return false 149 } 150 151 for _, child := range me.Children { 152 if child.isSamePage(pm.p) { 153 return true 154 } 155 156 if pm.HasMenuCurrent(menuID, child) { 157 return true 158 } 159 } 160 161 return false 162 } 163 164 func (pm *pageMenus) IsMenuCurrent(menuID string, inme *MenuEntry) bool { 165 menus := pm.pagem.Menus() 166 167 if me, ok := menus[menuID]; ok { 168 if me.IsEqual(inme) { 169 return true 170 } 171 } 172 173 if pm.p == nil { 174 return false 175 } 176 177 if !inme.isSamePage(pm.p) { 178 return false 179 } 180 181 // This resource may be included in several menus. 182 // Search for it to make sure that it is in the menu with the given menuId. 183 if menu, ok := pm.sitem.Menus()[menuID]; ok { 184 for _, menuEntry := range menu { 185 if menuEntry.IsSameResource(inme) { 186 return true 187 } 188 189 descendantFound := pm.isSameAsDescendantMenu(inme, menuEntry) 190 if descendantFound { 191 return descendantFound 192 } 193 194 } 195 } 196 197 return false 198 } 199 200 func (pm *pageMenus) isSameAsDescendantMenu(inme *MenuEntry, parent *MenuEntry) bool { 201 if parent.HasChildren() { 202 for _, child := range parent.Children { 203 if child.IsSameResource(inme) { 204 return true 205 } 206 descendantFound := pm.isSameAsDescendantMenu(inme, child) 207 if descendantFound { 208 return descendantFound 209 } 210 } 211 } 212 return false 213 } 214 215 var NopPageMenus = new(nopPageMenus) 216 217 type nopPageMenus int 218 219 func (m nopPageMenus) Menus() PageMenus { 220 return PageMenus{} 221 } 222 223 func (m nopPageMenus) HasMenuCurrent(menuID string, me *MenuEntry) bool { 224 return false 225 } 226 227 func (m nopPageMenus) IsMenuCurrent(menuID string, inme *MenuEntry) bool { 228 return false 229 }