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  }