github.com/gocaveman/caveman@v0.0.0-20191211162744-0ddf99dbdf6e/menus/menus.go (about)

     1  package menus
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/gocaveman/caveman/webutil"
     7  )
     8  
     9  var ErrNotFound = webutil.ErrNotFound
    10  var ErrAlreadyExists = fmt.Errorf("already exists")
    11  
    12  type Store interface {
    13  	ReadMenuItem(id string) (*MenuItem, error)
    14  	CreateMenuItem(mi *MenuItem) error
    15  	UpdateMenuItem(mi *MenuItem) error
    16  	DeleteMenuItem(id string) error
    17  	// FIXME: Do we need a recursive delete?  If not, do we allow
    18  	// deletes for ones that have children?  We could just block that
    19  	// action and provide a separate naked function to do a recurive delete.
    20  	FindChildren(id string) ([]string, error)
    21  }
    22  
    23  type MenuItemReader interface {
    24  	ReadMenuItem(id string) (*MenuItem, error)
    25  	FindChildren(id string) ([]string, error)
    26  }
    27  
    28  type MenuItem struct {
    29  	MenuID       string                      `json:"menu_id" yaml:"menu_id" db:"menu_id"`
    30  	ParentMenuID string                      `json:"parent_menu_id" yaml:"parent_menu_id" db:"parent_menu_id"`
    31  	Sequence     float64                     `json:"sequence" yaml:"sequence" db:"sequence"`
    32  	Title        string                      `json:"title" yaml:"title" db:"title"`
    33  	Meta         webutil.SimpleStringDataMap `json:"meta" yaml:"meta" db:"meta"`
    34  	Enabled      bool                        `json:"enabled" yaml:"enabled" db:"enabled"`
    35  }
    36  
    37  type MenuItemList []MenuItem
    38  
    39  // TODO: We could probably add some builder pattern stuff here so you can do things
    40  // like WithChildren() or other things that would make statically providing some menus
    41  // in a package to the registry easier to do.
    42  
    43  func (a MenuItemList) Len() int      { return len(a) }
    44  func (a MenuItemList) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
    45  func (a MenuItemList) Less(i, j int) bool {
    46  	if a[i].Sequence == a[j].Sequence {
    47  		return a[i].MenuID < a[j].MenuID
    48  	}
    49  	return a[i].Sequence < a[j].Sequence
    50  }
    51  
    52  // TODO: MenuScopeHandler to provide MenuScope in context,
    53  // and then register it in the handlerregistry and autowire.
    54  
    55  // MenuScope provides display facilities for use in templates.
    56  type MenuScope struct {
    57  	MenuItemReader `autowire:""`
    58  }
    59  
    60  func (ms *MenuScope) Display(id string) (DisplayMenuItem, error) {
    61  	return BuildDisplayMenuItem(ms.MenuItemReader, id)
    62  }
    63  
    64  type DisplayMenuItem struct {
    65  	MenuItem
    66  	Children   []DisplayMenuItem      `json:"children,omitempty" yaml:"children,omitempty"`
    67  	Additional map[string]interface{} `json:"additional,omitempty" yaml:"additional,omitempty"`
    68  }
    69  
    70  func BuildDisplayMenuItem(r MenuItemReader, id string) (ret DisplayMenuItem, err error) {
    71  
    72  	var mi *MenuItem
    73  	mi, err = r.ReadMenuItem(id)
    74  	if err != nil {
    75  		return
    76  	}
    77  	ret.MenuItem = *mi
    78  
    79  	var childIDs []string
    80  	childIDs, err = r.FindChildren(id)
    81  	if err != nil {
    82  		return
    83  	}
    84  
    85  	for _, childID := range childIDs {
    86  		var child DisplayMenuItem
    87  		child, err = BuildDisplayMenuItem(r, childID)
    88  		if err != nil {
    89  			return
    90  		}
    91  		ret.Children = append(ret.Children, child)
    92  	}
    93  
    94  	return
    95  }