Merge pull request #837 from bsingr/develop
Insert pagination utilities from beego/wetalk. Refs #835.
Showing
4 changed files
with
308 additions
and
0 deletions
utils/pagination/controller.go
0 → 100644
| 1 | // Copyright 2014 beego Author. 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 | // | ||
| 7 | // http://www.apache.org/licenses/LICENSE-2.0 | ||
| 8 | // | ||
| 9 | // Unless required by applicable law or agreed to in writing, software | ||
| 10 | // distributed under the License is distributed on an "AS IS" BASIS, | ||
| 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 12 | // See the License for the specific language governing permissions and | ||
| 13 | // limitations under the License. | ||
| 14 | |||
| 15 | package pagination | ||
| 16 | |||
| 17 | import ( | ||
| 18 | "github.com/astaxie/beego/context" | ||
| 19 | ) | ||
| 20 | |||
| 21 | // Instantiates a Paginator and assigns it to context.Input.Data["paginator"]. | ||
| 22 | func SetPaginator(context *context.Context, per int, nums int64) (paginator *Paginator) { | ||
| 23 | paginator = NewPaginator(context.Request, per, nums) | ||
| 24 | context.Input.Data["paginator"] = paginator | ||
| 25 | return | ||
| 26 | } |
utils/pagination/doc.go
0 → 100644
| 1 | /* | ||
| 2 | |||
| 3 | The pagination package provides utilities to setup a paginator within the | ||
| 4 | context of a http request. | ||
| 5 | |||
| 6 | Usage | ||
| 7 | |||
| 8 | In your beego.Controller: | ||
| 9 | |||
| 10 | package controllers | ||
| 11 | |||
| 12 | import "github.com/astaxie/beego/utils/pagination" | ||
| 13 | |||
| 14 | type PostsController struct { | ||
| 15 | beego.Controller | ||
| 16 | } | ||
| 17 | |||
| 18 | func (this *PostsController) ListAllPosts() { | ||
| 19 | // sets this.Data["paginator"] with the current offset (from the url query param) | ||
| 20 | postsPerPage := 20 | ||
| 21 | paginator := pagination.SetPaginator(this.Ctx, postsPerPage, CountPosts()) | ||
| 22 | |||
| 23 | // fetch the next 20 posts | ||
| 24 | this.Data["posts"] = ListPostsByOffsetAndLimit(paginator.Offset(), postsPerPage) | ||
| 25 | } | ||
| 26 | |||
| 27 | |||
| 28 | In your view templates: | ||
| 29 | |||
| 30 | {{if .paginator.HasPages}} | ||
| 31 | <ul class="pagination pagination"> | ||
| 32 | {{if .paginator.HasPrev}} | ||
| 33 | <li><a href="{{.paginator.PageLinkFirst}}">{{ i18n .Lang "paginator.first_page"}}</a></li> | ||
| 34 | <li><a href="{{.paginator.PageLinkPrev}}">«</a></li> | ||
| 35 | {{else}} | ||
| 36 | <li class="disabled"><a>{{ i18n .Lang "paginator.first_page"}}</a></li> | ||
| 37 | <li class="disabled"><a>«</a></li> | ||
| 38 | {{end}} | ||
| 39 | {{range $index, $page := .paginator.Pages}} | ||
| 40 | <li{{if $.paginator.IsActive .}} class="active"{{end}}> | ||
| 41 | <a href="{{$.paginator.PageLink $page}}">{{$page}}</a> | ||
| 42 | </li> | ||
| 43 | {{end}} | ||
| 44 | {{if .paginator.HasNext}} | ||
| 45 | <li><a href="{{.paginator.PageLinkNext}}">»</a></li> | ||
| 46 | <li><a href="{{.paginator.PageLinkLast}}">{{ i18n .Lang "paginator.last_page"}}</a></li> | ||
| 47 | {{else}} | ||
| 48 | <li class="disabled"><a>»</a></li> | ||
| 49 | <li class="disabled"><a>{{ i18n .Lang "paginator.last_page"}}</a></li> | ||
| 50 | {{end}} | ||
| 51 | </ul> | ||
| 52 | {{end}} | ||
| 53 | |||
| 54 | See also | ||
| 55 | |||
| 56 | http://beego.me/docs/mvc/view/page.md | ||
| 57 | |||
| 58 | */ | ||
| 59 | package pagination |
utils/pagination/paginator.go
0 → 100644
| 1 | // Copyright 2014 beego Author. 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 | // | ||
| 7 | // http://www.apache.org/licenses/LICENSE-2.0 | ||
| 8 | // | ||
| 9 | // Unless required by applicable law or agreed to in writing, software | ||
| 10 | // distributed under the License is distributed on an "AS IS" BASIS, | ||
| 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 12 | // See the License for the specific language governing permissions and | ||
| 13 | // limitations under the License. | ||
| 14 | |||
| 15 | package pagination | ||
| 16 | |||
| 17 | import ( | ||
| 18 | "math" | ||
| 19 | "net/http" | ||
| 20 | "net/url" | ||
| 21 | "strconv" | ||
| 22 | ) | ||
| 23 | |||
| 24 | // Paginator within the state of a http request. | ||
| 25 | type Paginator struct { | ||
| 26 | Request *http.Request | ||
| 27 | PerPageNums int | ||
| 28 | MaxPages int | ||
| 29 | |||
| 30 | nums int64 | ||
| 31 | pageRange []int | ||
| 32 | pageNums int | ||
| 33 | page int | ||
| 34 | } | ||
| 35 | |||
| 36 | // Returns the total number of pages. | ||
| 37 | func (p *Paginator) PageNums() int { | ||
| 38 | if p.pageNums != 0 { | ||
| 39 | return p.pageNums | ||
| 40 | } | ||
| 41 | pageNums := math.Ceil(float64(p.nums) / float64(p.PerPageNums)) | ||
| 42 | if p.MaxPages > 0 { | ||
| 43 | pageNums = math.Min(pageNums, float64(p.MaxPages)) | ||
| 44 | } | ||
| 45 | p.pageNums = int(pageNums) | ||
| 46 | return p.pageNums | ||
| 47 | } | ||
| 48 | |||
| 49 | // Returns the total number of items (e.g. from doing SQL count). | ||
| 50 | func (p *Paginator) Nums() int64 { | ||
| 51 | return p.nums | ||
| 52 | } | ||
| 53 | |||
| 54 | // Sets the total number of items. | ||
| 55 | func (p *Paginator) SetNums(nums interface{}) { | ||
| 56 | p.nums, _ = ToInt64(nums) | ||
| 57 | } | ||
| 58 | |||
| 59 | // Returns the current page. | ||
| 60 | func (p *Paginator) Page() int { | ||
| 61 | if p.page != 0 { | ||
| 62 | return p.page | ||
| 63 | } | ||
| 64 | if p.Request.Form == nil { | ||
| 65 | p.Request.ParseForm() | ||
| 66 | } | ||
| 67 | p.page, _ = strconv.Atoi(p.Request.Form.Get("p")) | ||
| 68 | if p.page > p.PageNums() { | ||
| 69 | p.page = p.PageNums() | ||
| 70 | } | ||
| 71 | if p.page <= 0 { | ||
| 72 | p.page = 1 | ||
| 73 | } | ||
| 74 | return p.page | ||
| 75 | } | ||
| 76 | |||
| 77 | // Returns a list of all pages. | ||
| 78 | // | ||
| 79 | // Usage (in a view template): | ||
| 80 | // | ||
| 81 | // {{range $index, $page := .paginator.Pages}} | ||
| 82 | // <li{{if $.paginator.IsActive .}} class="active"{{end}}> | ||
| 83 | // <a href="{{$.paginator.PageLink $page}}">{{$page}}</a> | ||
| 84 | // </li> | ||
| 85 | // {{end}} | ||
| 86 | func (p *Paginator) Pages() []int { | ||
| 87 | if p.pageRange == nil && p.nums > 0 { | ||
| 88 | var pages []int | ||
| 89 | pageNums := p.PageNums() | ||
| 90 | page := p.Page() | ||
| 91 | switch { | ||
| 92 | case page >= pageNums-4 && pageNums > 9: | ||
| 93 | start := pageNums - 9 + 1 | ||
| 94 | pages = make([]int, 9) | ||
| 95 | for i, _ := range pages { | ||
| 96 | pages[i] = start + i | ||
| 97 | } | ||
| 98 | case page >= 5 && pageNums > 9: | ||
| 99 | start := page - 5 + 1 | ||
| 100 | pages = make([]int, int(math.Min(9, float64(page+4+1)))) | ||
| 101 | for i, _ := range pages { | ||
| 102 | pages[i] = start + i | ||
| 103 | } | ||
| 104 | default: | ||
| 105 | pages = make([]int, int(math.Min(9, float64(pageNums)))) | ||
| 106 | for i, _ := range pages { | ||
| 107 | pages[i] = i + 1 | ||
| 108 | } | ||
| 109 | } | ||
| 110 | p.pageRange = pages | ||
| 111 | } | ||
| 112 | return p.pageRange | ||
| 113 | } | ||
| 114 | |||
| 115 | // Returns URL for a given page index. | ||
| 116 | func (p *Paginator) PageLink(page int) string { | ||
| 117 | link, _ := url.ParseRequestURI(p.Request.RequestURI) | ||
| 118 | values := link.Query() | ||
| 119 | if page == 1 { | ||
| 120 | values.Del("p") | ||
| 121 | } else { | ||
| 122 | values.Set("p", strconv.Itoa(page)) | ||
| 123 | } | ||
| 124 | link.RawQuery = values.Encode() | ||
| 125 | return link.String() | ||
| 126 | } | ||
| 127 | |||
| 128 | // Returns URL to the previous page. | ||
| 129 | func (p *Paginator) PageLinkPrev() (link string) { | ||
| 130 | if p.HasPrev() { | ||
| 131 | link = p.PageLink(p.Page() - 1) | ||
| 132 | } | ||
| 133 | return | ||
| 134 | } | ||
| 135 | |||
| 136 | // Returns URL to the next page. | ||
| 137 | func (p *Paginator) PageLinkNext() (link string) { | ||
| 138 | if p.HasNext() { | ||
| 139 | link = p.PageLink(p.Page() + 1) | ||
| 140 | } | ||
| 141 | return | ||
| 142 | } | ||
| 143 | |||
| 144 | // Returns URL to the first page. | ||
| 145 | func (p *Paginator) PageLinkFirst() (link string) { | ||
| 146 | return p.PageLink(1) | ||
| 147 | } | ||
| 148 | |||
| 149 | // Returns URL to the last page. | ||
| 150 | func (p *Paginator) PageLinkLast() (link string) { | ||
| 151 | return p.PageLink(p.PageNums()) | ||
| 152 | } | ||
| 153 | |||
| 154 | // Returns true if the current page has a predecessor. | ||
| 155 | func (p *Paginator) HasPrev() bool { | ||
| 156 | return p.Page() > 1 | ||
| 157 | } | ||
| 158 | |||
| 159 | // Returns true if the current page has a successor. | ||
| 160 | func (p *Paginator) HasNext() bool { | ||
| 161 | return p.Page() < p.PageNums() | ||
| 162 | } | ||
| 163 | |||
| 164 | // Returns true if the given page index points to the current page. | ||
| 165 | func (p *Paginator) IsActive(page int) bool { | ||
| 166 | return p.Page() == page | ||
| 167 | } | ||
| 168 | |||
| 169 | // Returns the current offset. | ||
| 170 | func (p *Paginator) Offset() int { | ||
| 171 | return (p.Page() - 1) * p.PerPageNums | ||
| 172 | } | ||
| 173 | |||
| 174 | // Returns true if there is more than one page. | ||
| 175 | func (p *Paginator) HasPages() bool { | ||
| 176 | return p.PageNums() > 1 | ||
| 177 | } | ||
| 178 | |||
| 179 | // Instantiates a paginator struct for the current http request. | ||
| 180 | func NewPaginator(req *http.Request, per int, nums interface{}) *Paginator { | ||
| 181 | p := Paginator{} | ||
| 182 | p.Request = req | ||
| 183 | if per <= 0 { | ||
| 184 | per = 10 | ||
| 185 | } | ||
| 186 | p.PerPageNums = per | ||
| 187 | p.SetNums(nums) | ||
| 188 | return &p | ||
| 189 | } |
utils/pagination/utils.go
0 → 100644
| 1 | // Copyright 2014 beego Author. 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 | // | ||
| 7 | // http://www.apache.org/licenses/LICENSE-2.0 | ||
| 8 | // | ||
| 9 | // Unless required by applicable law or agreed to in writing, software | ||
| 10 | // distributed under the License is distributed on an "AS IS" BASIS, | ||
| 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 12 | // See the License for the specific language governing permissions and | ||
| 13 | // limitations under the License. | ||
| 14 | |||
| 15 | package pagination | ||
| 16 | |||
| 17 | import ( | ||
| 18 | "fmt" | ||
| 19 | "reflect" | ||
| 20 | ) | ||
| 21 | |||
| 22 | // convert any numeric value to int64 | ||
| 23 | func ToInt64(value interface{}) (d int64, err error) { | ||
| 24 | val := reflect.ValueOf(value) | ||
| 25 | switch value.(type) { | ||
| 26 | case int, int8, int16, int32, int64: | ||
| 27 | d = val.Int() | ||
| 28 | case uint, uint8, uint16, uint32, uint64: | ||
| 29 | d = int64(val.Uint()) | ||
| 30 | default: | ||
| 31 | err = fmt.Errorf("ToInt64 need numeric not `%T`", value) | ||
| 32 | } | ||
| 33 | return | ||
| 34 | } |
-
Please register or sign in to post a comment