hugo

Fork of github.com/gohugoio/hugo with reverse pagination support

git clone git://git.shimmy1996.com/hugo.git

pagemenus.go (4543B)

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