From 6fca4a82186c3633f1cb5bdf6a0eb79f8c0529e9 Mon Sep 17 00:00:00 2001 From: Jens Bissinger Date: Thu, 2 Oct 2014 11:40:46 +0200 Subject: [PATCH] Insert pagination utilities from beego/wetalk. Refs #835. --- controller.go | 8 ++ pagination/controller.go | 32 ++++++++ pagination/paginator.go | 164 +++++++++++++++++++++++++++++++++++++++ pagination/utils.go | 20 +++++ 4 files changed, 224 insertions(+) create mode 100644 pagination/controller.go create mode 100644 pagination/paginator.go create mode 100644 pagination/utils.go diff --git a/controller.go b/controller.go index 72ba323b..e7eaa468 100644 --- a/controller.go +++ b/controller.go @@ -93,6 +93,14 @@ type ControllerInterface interface { URLMapping() } +func (c *Controller) GetCtx() *context.Context { + return c.Ctx +} + +func (c *Controller) GetData() map[interface{}]interface{} { + return c.Data +} + // Init generates default values of controller operations. func (c *Controller) Init(ctx *context.Context, controllerName, actionName string, app interface{}) { c.Layout = "" diff --git a/pagination/controller.go b/pagination/controller.go new file mode 100644 index 00000000..794d779d --- /dev/null +++ b/pagination/controller.go @@ -0,0 +1,32 @@ +// Copyright 2014 beego Author. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package pagination + +import ( + "github.com/astaxie/beego/context" +) + +type PaginationController interface { + GetCtx() *context.Context + GetData() map[interface{}]interface{} +} + +func SetPaginator(controller PaginationController, per int, nums int64) (paginator *Paginator) { + request := controller.GetCtx().Request + paginator = NewPaginator(request, per, nums) + data := controller.GetData() + data["paginator"] = paginator + return +} diff --git a/pagination/paginator.go b/pagination/paginator.go new file mode 100644 index 00000000..593f587d --- /dev/null +++ b/pagination/paginator.go @@ -0,0 +1,164 @@ +// Copyright 2014 beego Author. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package pagination + +import ( + "math" + "net/http" + "net/url" + "strconv" +) + +type Paginator struct { + Request *http.Request + PerPageNums int + MaxPages int + + nums int64 + pageRange []int + pageNums int + page int +} + +func (p *Paginator) PageNums() int { + if p.pageNums != 0 { + return p.pageNums + } + pageNums := math.Ceil(float64(p.nums) / float64(p.PerPageNums)) + if p.MaxPages > 0 { + pageNums = math.Min(pageNums, float64(p.MaxPages)) + } + p.pageNums = int(pageNums) + return p.pageNums +} + +func (p *Paginator) Nums() int64 { + return p.nums +} + +func (p *Paginator) SetNums(nums interface{}) { + p.nums, _ = ToInt64(nums) +} + +func (p *Paginator) Page() int { + if p.page != 0 { + return p.page + } + if p.Request.Form == nil { + p.Request.ParseForm() + } + p.page, _ = strconv.Atoi(p.Request.Form.Get("p")) + if p.page > p.PageNums() { + p.page = p.PageNums() + } + if p.page <= 0 { + p.page = 1 + } + return p.page +} + +func (p *Paginator) Pages() []int { + if p.pageRange == nil && p.nums > 0 { + var pages []int + pageNums := p.PageNums() + page := p.Page() + switch { + case page >= pageNums-4 && pageNums > 9: + start := pageNums - 9 + 1 + pages = make([]int, 9) + for i, _ := range pages { + pages[i] = start + i + } + case page >= 5 && pageNums > 9: + start := page - 5 + 1 + pages = make([]int, int(math.Min(9, float64(page+4+1)))) + for i, _ := range pages { + pages[i] = start + i + } + default: + pages = make([]int, int(math.Min(9, float64(pageNums)))) + for i, _ := range pages { + pages[i] = i + 1 + } + } + p.pageRange = pages + } + return p.pageRange +} + +func (p *Paginator) PageLink(page int) string { + link, _ := url.ParseRequestURI(p.Request.RequestURI) + values := link.Query() + if page == 1 { + values.Del("p") + } else { + values.Set("p", strconv.Itoa(page)) + } + link.RawQuery = values.Encode() + return link.String() +} + +func (p *Paginator) PageLinkPrev() (link string) { + if p.HasPrev() { + link = p.PageLink(p.Page() - 1) + } + return +} + +func (p *Paginator) PageLinkNext() (link string) { + if p.HasNext() { + link = p.PageLink(p.Page() + 1) + } + return +} + +func (p *Paginator) PageLinkFirst() (link string) { + return p.PageLink(1) +} + +func (p *Paginator) PageLinkLast() (link string) { + return p.PageLink(p.PageNums()) +} + +func (p *Paginator) HasPrev() bool { + return p.Page() > 1 +} + +func (p *Paginator) HasNext() bool { + return p.Page() < p.PageNums() +} + +func (p *Paginator) IsActive(page int) bool { + return p.Page() == page +} + +func (p *Paginator) Offset() int { + return (p.Page() - 1) * p.PerPageNums +} + +func (p *Paginator) HasPages() bool { + return p.PageNums() > 1 +} + +func NewPaginator(req *http.Request, per int, nums interface{}) *Paginator { + p := Paginator{} + p.Request = req + if per <= 0 { + per = 10 + } + p.PerPageNums = per + p.SetNums(nums) + return &p +} diff --git a/pagination/utils.go b/pagination/utils.go new file mode 100644 index 00000000..d1199f2d --- /dev/null +++ b/pagination/utils.go @@ -0,0 +1,20 @@ +package pagination + +import ( + "fmt" + "reflect" +) + +// convert any numeric value to int64 +func ToInt64(value interface{}) (d int64, err error) { + val := reflect.ValueOf(value) + switch value.(type) { + case int, int8, int16, int32, int64: + d = val.Int() + case uint, uint8, uint16, uint32, uint64: + d = int64(val.Uint()) + default: + err = fmt.Errorf("ToInt64 need numeric not `%T`", value) + } + return +}