diff --git a/models/host.go b/models/host.go index f16e1b0..9c8935f 100644 --- a/models/host.go +++ b/models/host.go @@ -18,8 +18,7 @@ type Host struct { Remark string `xorm:"varchar(512) notnull default '' "` // 备注 AuthType ssh.HostAuthType `xorm:"tinyint notnull default 1"` // 认证方式 1: 密码 2: 公钥 PrivateKey string `xorm:"varchar(4096) notnull default '' "` // 私钥 - Page int `xorm:"-"` - PageSize int `xorm:"-"` + BaseModel `xorm:"-"` } // 新增 @@ -64,7 +63,7 @@ func (host *Host) NameExists(name string, id int16) (bool, error) { } func (host *Host) List(params CommonMap) ([]Host, error) { - host.parsePageAndPageSize() + host.parsePageAndPageSize(params) list := make([]Host, 0) session := Db.Desc("id") host.parseWhere(session, params) @@ -74,15 +73,16 @@ func (host *Host) List(params CommonMap) ([]Host, error) { } func (host *Host) AllList() ([]Host, error) { - host.parsePageAndPageSize() list := make([]Host, 0) err := Db.Desc("id").Find(&list) return list, err } -func (host *Host) Total() (int64, error) { - return Db.Count(host) +func (host *Host) Total(params CommonMap) (int64, error) { + session := Db.NewSession() + host.parseWhere(session, params) + return session.Count(host) } // 解析where @@ -98,17 +98,4 @@ func (host *Host) parseWhere(session *xorm.Session, params CommonMap) { if ok && name.(string) != "" { session.And("name = ?", name) } -} - -func (host *Host) parsePageAndPageSize() { - if host.Page <= 0 { - host.Page = Page - } - if host.PageSize >= 0 || host.PageSize > MaxPageSize { - host.PageSize = PageSize - } -} - -func (host *Host) pageLimitOffset() int { - return (host.Page - 1) * host.PageSize -} +} \ No newline at end of file diff --git a/models/model.go b/models/model.go index fff9f5a..e1d2be1 100644 --- a/models/model.go +++ b/models/model.go @@ -31,6 +31,32 @@ const ( const DefaultTimeFormat = "2006-01-02 15:04:05" +type BaseModel struct { + Page int `xorm:"-"` + PageSize int `xorm:"-"` +} + +func (model *BaseModel) parsePageAndPageSize(params CommonMap) { + page, ok := params["Page"] + if ok { + model.Page = page.(int) + } + pageSize, ok := params["PageSize"] + if ok { + model.PageSize = pageSize.(int) + } + if model.Page <= 0 { + model.Page = Page + } + if model.PageSize <= 0 || model.PageSize > MaxPageSize { + model.PageSize = PageSize + } +} + +func (model *BaseModel) pageLimitOffset() int { + return (model.Page - 1) * model.PageSize +} + // 创建Db func CreateDb(config map[string]string) *xorm.Engine { dsn := getDbEngineDSN(config["engine"], config) @@ -79,7 +105,7 @@ func getDbEngineDSN(engine string, config map[string]string) string { return dsn } -// 定时ping, 防止因数据库超时设置被断开 +// 定时ping, 防止因数据库超时设置连接被断开 func keepDbAlived(engine *xorm.Engine) { t := time.Tick(180 * time.Second) for { diff --git a/models/task.go b/models/task.go index 34374ed..7c0e234 100644 --- a/models/task.go +++ b/models/task.go @@ -28,8 +28,7 @@ type Task struct { Created time.Time `xorm:"datetime notnull created"` // 创建时间 Deleted time.Time `xorm:"datetime deleted"` // 删除时间 Status Status `xorm:"tinyint notnull default 1"` // 状态 1:正常 0:停止 - Page int `xorm:"-"` - PageSize int `xorm:"-"` + BaseModel `xorm:"-"` } type TaskHost struct { @@ -136,8 +135,10 @@ func (task *Task) List(params CommonMap) ([]TaskHost, error) { return list, err } -func (task *Task) Total() (int64, error) { - return Db.Count(task) +func (task *Task) Total(params CommonMap) (int64, error) { + session := Db.Alias("t").Join("LEFT", "host", "t.host_id=host.id") + task.parseWhere(session, params) + return session.Count(task) } // 解析where @@ -165,25 +166,4 @@ func (task *Task) parseWhere(session *xorm.Session, params CommonMap) { if ok && status.(int) > -1 { session.And("status = ?", status) } -} - -func (task *Task) parsePageAndPageSize(params CommonMap) { - page, ok := params["Page"] - if ok { - task.Page = page.(int) - } - pageSize, ok := params["PageSize"] - if ok { - task.PageSize = pageSize.(int) - } - if task.Page <= 0 { - task.Page = Page - } - if task.PageSize >= 0 || task.PageSize > MaxPageSize { - task.PageSize = PageSize - } -} - -func (task *Task) pageLimitOffset() int { - return (task.Page - 1) * task.PageSize } \ No newline at end of file diff --git a/models/task_log.go b/models/task_log.go index 2f9ae65..d626c4a 100644 --- a/models/task_log.go +++ b/models/task_log.go @@ -3,6 +3,7 @@ package models import ( "time" "github.com/go-xorm/xorm" + "github.com/ouqiang/gocron/modules/logger" ) type TaskType int8 @@ -23,9 +24,8 @@ type TaskLog struct { EndTime time.Time `xorm:"datetime updated"` // 执行完成(失败)时间 Status Status `xorm:"tinyint notnull default 1"` // 状态 0:执行失败 1:执行中 2:执行完毕 Result string `xorm:"varchar(65535) notnull defalut '' "` // 执行结果 - Page int `xorm:"-"` - PageSize int `xorm:"-"` TotalTime int `xorm:"-"` // 执行总时长 + BaseModel `xorm:"-"` } func (taskLog *TaskLog) Create() (insertId int64, err error) { @@ -49,7 +49,7 @@ func (taskLog *TaskLog) setStatus(id int64, status Status) (int64, error) { } func (taskLog *TaskLog) List(params CommonMap) ([]TaskLog, error) { - taskLog.parsePageAndPageSize() + taskLog.parsePageAndPageSize(params) list := make([]TaskLog, 0) session := Db.Desc("id") taskLog.parseWhere(session, params) @@ -73,21 +73,10 @@ func (TaskLog *TaskLog) Clear() (int64, error) { return Db.Where("1=1").Delete(TaskLog); } -func (taskLog *TaskLog) Total() (int64, error) { - return Db.Count(taskLog) -} - -func (taskLog *TaskLog) parsePageAndPageSize() { - if taskLog.Page <= 0 { - taskLog.Page = Page - } - if taskLog.PageSize >= 0 || taskLog.PageSize > MaxPageSize { - taskLog.PageSize = PageSize - } -} - -func (taskLog *TaskLog) pageLimitOffset() int { - return (taskLog.Page - 1) * taskLog.PageSize +func (taskLog *TaskLog) Total(params CommonMap) (int64, error) { + session := Db.NewSession() + taskLog.parseWhere(session, params) + return session.Count(taskLog) } // 解析where diff --git a/public/resource/javascript/main.js b/public/resource/javascript/main.js index 6ba72de..13116d1 100644 --- a/public/resource/javascript/main.js +++ b/public/resource/javascript/main.js @@ -6,6 +6,7 @@ function Util() { var util = {}; var SUCCESS = 0; // 操作成功 var FAILURE_MESSAGE = '操作失败'; + // ajax成功处理 util.ajaxSuccess = function(response, callback) { if (response.code === undefined) { swal(FAILURE_MESSAGE, '服务端返回值无法解析', 'error'); @@ -19,10 +20,12 @@ function Util() { callback(response.code, response.message, response.data); } }; + // ajax错误处理 util.ajaxFailure = function() { // todo 错误处理 swal(FAILURE_MESSAGE, '未知错误', 'error'); }; + // get请求 util.get = function(url, callback) { $.get( url, @@ -32,16 +35,18 @@ function Util() { 'json' ).error(util.ajaxFailure); }; + // post请求 util.post = function(url, params, callback) { $.post( url, - params, + util.objectTrim(params), function(response) { util.ajaxSuccess(response, callback); }, 'json' ).error(util.ajaxFailure); }; + // 弹出确认框 util.confirm = function(message, callback) { swal({ title: '操作确认', @@ -63,6 +68,7 @@ function Util() { } ); }; + // 删除确认 util.removeConfirm = function(url) { util.confirm("确定要删除吗?", function () { util.post(url, {}, function () { @@ -71,9 +77,19 @@ function Util() { }); }; + // 剔除对象元素首尾空格 + util.objectTrim = function(fields) { + for (key in fields) { + fields[key] = $.trim(fields[key]); + } + + return fields; + }; + return util; } +// 验证select关联字段 function registerSelectFormValidation(type, $form, $select, selectName) { $.fn.form.settings.rules[type] = function(value) { var success = true; diff --git a/routers/host/host.go b/routers/host/host.go index 517d43e..cf0f0bd 100644 --- a/routers/host/host.go +++ b/routers/host/host.go @@ -8,15 +8,29 @@ import ( "strconv" "github.com/ouqiang/gocron/modules/ssh" "github.com/ouqiang/gocron/service" + "github.com/Unknwon/paginater" + "fmt" + "html/template" ) func Index(ctx *macaron.Context) { hostModel := new(models.Host) queryParams := parseQueryParams(ctx) + total, err := hostModel.Total(queryParams) hosts, err := hostModel.List(queryParams) if err != nil { logger.Error(err) } + name, ok := queryParams["name"].(string) + var safeNameHTML = "" + if ok { + safeNameHTML = template.HTMLEscapeString(name) + } + PageParams := fmt.Sprintf("id=%d&name=%s&page_size=%d", + queryParams["Id"], safeNameHTML, queryParams["PageSize"]); + queryParams["PageParams"] = template.URL(PageParams) + p := paginater.New(int(total), queryParams["PageSize"].(int), queryParams["Page"].(int), 5) + ctx.Data["Pagination"] = p ctx.Data["Title"] = "主机列表" ctx.Data["Hosts"] = hosts ctx.Data["Params"] = queryParams @@ -151,8 +165,17 @@ func parseQueryParams(ctx *macaron.Context) (models.CommonMap) { var params models.CommonMap = models.CommonMap{} params["Id"] = ctx.QueryInt("id") params["Name"] = ctx.QueryTrim("name") - params["Page"] = ctx.QueryInt("page") - params["PageSize"] = ctx.QueryInt("page_size") + page := ctx.QueryInt("page") + pageSize := ctx.QueryInt("page_size") + if page <= 0 { + page = 1 + } + if pageSize <= 0 { + pageSize = models.PageSize + } + + params["Page"] = page + params["PageSize"] = pageSize return params } \ No newline at end of file diff --git a/routers/routers.go b/routers/routers.go index b6cb8b6..425d132 100644 --- a/routers/routers.go +++ b/routers/routers.go @@ -13,6 +13,7 @@ import ( "github.com/go-macaron/toolbox" "strings" "github.com/ouqiang/gocron/modules/app" + "github.com/ouqiang/gocron/modules/logger" ) // 静态文件目录 @@ -43,6 +44,7 @@ func Register(m *macaron.Macaron) { ctx.Resp.Write([]byte(json.Failure(utils.ServerError, "网站暂时无法访问,请稍后再试"))) } }) + // 首页 m.Get("/", Home) // 系统安装 diff --git a/routers/task/task.go b/routers/task/task.go index eba19b1..84ad603 100644 --- a/routers/task/task.go +++ b/routers/task/task.go @@ -8,6 +8,9 @@ import ( "github.com/ouqiang/gocron/service" "strconv" "github.com/jakecoffman/cron" + "github.com/Unknwon/paginater" + "fmt" + "html/template" ) type TaskForm struct { @@ -23,13 +26,28 @@ type TaskForm struct { Status models.Status `binding:"In(1,2)"` } +// 首页 func Index(ctx *macaron.Context) { taskModel := new(models.Task) queryParams := parseQueryParams(ctx) + total, err := taskModel.Total(queryParams) + if err != nil { + logger.Error(err) + } tasks, err := taskModel.List(queryParams) if err != nil { logger.Error(err) } + name, ok := queryParams["name"].(string) + var safeNameHTML = "" + if ok { + safeNameHTML = template.HTMLEscapeString(name) + } + PageParams := fmt.Sprintf("id=%d&host_id=%d&name=%s&protocol=%d&status=%d&page_size=%d", + queryParams["Id"], queryParams["HostId"], safeNameHTML, queryParams["Protocol"], queryParams["Status"], queryParams["PageSize"]); + queryParams["PageParams"] = template.URL(PageParams) + p := paginater.New(int(total), queryParams["PageSize"].(int), queryParams["Page"].(int), 5) + ctx.Data["Pagination"] = p setHostsToTemplate(ctx) ctx.Data["Params"] = queryParams ctx.Data["Title"] = "任务列表" @@ -37,12 +55,14 @@ func Index(ctx *macaron.Context) { ctx.HTML(200, "task/index") } +// 新增页面 func Create(ctx *macaron.Context) { setHostsToTemplate(ctx) ctx.Data["Title"] = "添加任务" ctx.HTML(200, "task/task_form") } +// 编辑页面 func Edit(ctx *macaron.Context) { id := ctx.ParamsInt(":id") hostModel := new(models.Host) @@ -204,9 +224,22 @@ func parseQueryParams(ctx *macaron.Context) (models.CommonMap) { params["HostId"] = ctx.QueryInt("host_id") params["Name"] = ctx.QueryTrim("name") params["Protocol"] = ctx.QueryInt("protocol") - params["Status"] = ctx.QueryInt("status") - 1 - params["Page"] = ctx.QueryInt("page") - params["PageSize"] = ctx.QueryInt("page_size") + status := ctx.QueryInt("status") + if status >=0 { + status -= 1 + } + params["Status"] = status + page := ctx.QueryInt("page") + pageSize := ctx.QueryInt("page_size") + if page <= 0 { + page = 1 + } + if pageSize <= 0 { + pageSize = models.PageSize + } + + params["Page"] = page + params["PageSize"] = pageSize return params } diff --git a/routers/tasklog/task_log.go b/routers/tasklog/task_log.go index da32710..a8b5975 100644 --- a/routers/tasklog/task_log.go +++ b/routers/tasklog/task_log.go @@ -5,6 +5,9 @@ import ( "github.com/ouqiang/gocron/models" "github.com/ouqiang/gocron/modules/logger" "github.com/ouqiang/gocron/modules/utils" + "github.com/Unknwon/paginater" + "fmt" + "html/template" ) // @author qiang.ou @@ -13,10 +16,20 @@ import ( func Index(ctx *macaron.Context) { logModel := new(models.TaskLog) queryParams := parseQueryParams(ctx) + total, err := logModel.Total(queryParams) + if err != nil { + logger.Error(err) + } logs, err := logModel.List(queryParams) if err != nil { logger.Error(err) } + PageParams := fmt.Sprintf("task_id=%d&protocol=%d&status=%d&page_size=%d", + queryParams["TaskId"], queryParams["Protocol"], queryParams["Status"], + queryParams["PageSize"]); + queryParams["PageParams"] = template.URL(PageParams) + p := paginater.New(int(total), queryParams["PageSize"].(int), queryParams["Page"].(int), 5) + ctx.Data["Pagination"] = p ctx.Data["Title"] = "任务日志" ctx.Data["Logs"] = logs ctx.Data["Params"] = queryParams @@ -40,9 +53,22 @@ func parseQueryParams(ctx *macaron.Context) (models.CommonMap) { var params models.CommonMap = models.CommonMap{} params["TaskId"] = ctx.QueryInt("task_id") params["Protocol"] = ctx.QueryInt("protocol") - params["Status"] = ctx.QueryInt("status") - 1 - params["Page"] = ctx.QueryInt("page") - params["PageSize"] = ctx.QueryInt("page_size") + status := ctx.QueryInt("status") + if status >=0 { + status -= 1 + } + params["Status"] = status + page := ctx.QueryInt("page") + pageSize := ctx.QueryInt("page_size") + if page <= 0 { + page = 1 + } + if pageSize <= 0 { + pageSize = models.PageSize + } + + params["Page"] = page + params["PageSize"] = pageSize return params } \ No newline at end of file diff --git a/templates/common/pagination.html b/templates/common/pagination.html new file mode 100644 index 0000000..dbc75e7 --- /dev/null +++ b/templates/common/pagination.html @@ -0,0 +1,37 @@ + diff --git a/templates/host/host_form.html b/templates/host/host_form.html index 5f46f3b..db1487b 100644 --- a/templates/host/host_form.html +++ b/templates/host/host_form.html @@ -7,10 +7,9 @@ @@ -19,13 +18,13 @@
-
+
-
+
@@ -33,13 +32,13 @@
-
+
-
+
@@ -56,7 +55,7 @@
-
+
@@ -64,7 +63,7 @@
-
+
@@ -72,7 +71,7 @@
-
+
diff --git a/templates/host/index.html b/templates/host/index.html index 22117d2..56f5a49 100644 --- a/templates/host/index.html +++ b/templates/host/index.html @@ -7,10 +7,12 @@ @@ -60,6 +62,7 @@ {{{end}}} + {{{ template "common/pagination" .}}}
diff --git a/templates/host/menu.html b/templates/host/menu.html index be31601..e9f7c11 100644 --- a/templates/host/menu.html +++ b/templates/host/menu.html @@ -4,9 +4,6 @@ 主机列表 - - 添加主机 -
\ No newline at end of file diff --git a/templates/task/index.html b/templates/task/index.html index 2c38406..f0a21c2 100644 --- a/templates/task/index.html +++ b/templates/task/index.html @@ -5,10 +5,12 @@ @@ -81,6 +83,7 @@
{{{end}}} + {{{ template "common/pagination" .}}}
diff --git a/templates/task/log.html b/templates/task/log.html index d128087..849ce22 100644 --- a/templates/task/log.html +++ b/templates/task/log.html @@ -103,6 +103,7 @@ {{{end}}} + {{{ template "common/pagination" .}}}
diff --git a/templates/task/menu.html b/templates/task/menu.html index 34ac898..fe66509 100644 --- a/templates/task/menu.html +++ b/templates/task/menu.html @@ -4,9 +4,6 @@ 任务列表 - - 添加任务 - 任务日志 diff --git a/templates/task/task_form.html b/templates/task/task_form.html index e9e41c2..ad1cacc 100644 --- a/templates/task/task_form.html +++ b/templates/task/task_form.html @@ -5,7 +5,6 @@
-
+
diff --git a/templates/user/login.html b/templates/user/login.html new file mode 100644 index 0000000..3738803 --- /dev/null +++ b/templates/user/login.html @@ -0,0 +1,4 @@ +{{{ template "common/header" . }}} + + +{{{ template "common/footer" . }}} \ No newline at end of file diff --git a/vendor/github.com/Unknwon/paginater/LICENSE b/vendor/github.com/Unknwon/paginater/LICENSE new file mode 100644 index 0000000..8f71f43 --- /dev/null +++ b/vendor/github.com/Unknwon/paginater/LICENSE @@ -0,0 +1,202 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + 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. + diff --git a/vendor/github.com/Unknwon/paginater/Makefile b/vendor/github.com/Unknwon/paginater/Makefile new file mode 100644 index 0000000..f5f7ee0 --- /dev/null +++ b/vendor/github.com/Unknwon/paginater/Makefile @@ -0,0 +1,9 @@ +.PHONY: build test vet + +build: vet + +test: + go test -v -cover -race + +vet: + go vet \ No newline at end of file diff --git a/vendor/github.com/Unknwon/paginater/README.md b/vendor/github.com/Unknwon/paginater/README.md new file mode 100644 index 0000000..3886d7b --- /dev/null +++ b/vendor/github.com/Unknwon/paginater/README.md @@ -0,0 +1,65 @@ +Paginater [![Build Status](https://travis-ci.org/Unknwon/paginater.svg?branch=master)](https://travis-ci.org/Unknwon/paginater) +========= + +Package paginater is a helper module for custom pagination calculation. + +## Installation + + go get github.com/Unknwon/paginater + +## Getting Started + +The following code shows an example of how to use paginater: + +```go +package main + +import "github.com/Unknwon/paginater" + +func main() { + // Arguments: + // - Total number of rows + // - Number of rows in one page + // - Current page number + // - Number of page links to be displayed + p := paginater.New(45, 10, 3, 3) + + // Then use p as a template object named "Page" in "demo.html" + // ... +} +``` + +`demo.html` + +```html +{{if not .Page.IsFirst}}[First](1){{end}} +{{if .Page.HasPrevious}}[Previous]({{.Page.Previous}}){{end}} + +{{range .Page.Pages}} + {{if eq .Num -1}} + ... + {{else}} + {{.Num}}{{if .IsCurrent}}(current){{end}} + {{end}} +{{end}} + +{{if .Page.HasNext}}[Next]({{.Page.Next}}){{end}} +{{if not .Page.IsLast}}[Last]({{.Page.TotalPages}}){{end}} +``` + +Possible output: + +``` +[First](1) [Previous](2) ... 2 3(current) 4 ... [Next](4) [Last](5) +``` + +As you may guess, if the `Page` value is `-1`, you should print `...` in the HTML as common practice. + +## Getting Help + +- [API Documentation](https://gowalker.org/github.com/Unknwon/paginater) +- [File An Issue](https://github.com/Unknwon/paginater/issues/new) + +## License + +This project is under Apache v2 License. See the [LICENSE](LICENSE) file for the full license text. \ No newline at end of file diff --git a/vendor/github.com/Unknwon/paginater/paginater.go b/vendor/github.com/Unknwon/paginater/paginater.go new file mode 100644 index 0000000..caeea73 --- /dev/null +++ b/vendor/github.com/Unknwon/paginater/paginater.go @@ -0,0 +1,197 @@ +// Copyright 2015 Unknwon +// +// 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 paginater is a helper module for custom pagination calculation. +package paginater + +// Paginater represents a set of results of pagination calculations. +type Paginater struct { + total int + pagingNum int + current int + numPages int +} + +// New initialize a new pagination calculation and returns a Paginater as result. +func New(total, pagingNum, current, numPages int) *Paginater { + if pagingNum <= 0 { + pagingNum = 1 + } + if current <= 0 { + current = 1 + } + p := &Paginater{total, pagingNum, current, numPages} + if p.current > p.TotalPages() { + p.current = p.TotalPages() + } + return p +} + +// IsFirst returns true if current page is the first page. +func (p *Paginater) IsFirst() bool { + return p.current == 1 +} + +// HasPrevious returns true if there is a previous page relative to current page. +func (p *Paginater) HasPrevious() bool { + return p.current > 1 +} + +func (p *Paginater) Previous() int { + if !p.HasPrevious() { + return p.current + } + return p.current - 1 +} + +// HasNext returns true if there is a next page relative to current page. +func (p *Paginater) HasNext() bool { + return p.total > p.current*p.pagingNum +} + +func (p *Paginater) Next() int { + if !p.HasNext() { + return p.current + } + return p.current + 1 +} + +// IsLast returns true if current page is the last page. +func (p *Paginater) IsLast() bool { + if p.total == 0 { + return true + } + return p.total > (p.current-1)*p.pagingNum && !p.HasNext() +} + +// Total returns number of total rows. +func (p *Paginater) Total() int { + return p.total +} + +// TotalPage returns number of total pages. +func (p *Paginater) TotalPages() int { + if p.total == 0 { + return 1 + } + if p.total%p.pagingNum == 0 { + return p.total / p.pagingNum + } + return p.total/p.pagingNum + 1 +} + +// Current returns current page number. +func (p *Paginater) Current() int { + return p.current +} + +// PagingNum returns number of page size. +func (p *Paginater) PagingNum() int { + return p.pagingNum +} + +// Page presents a page in the paginater. +type Page struct { + num int + isCurrent bool +} + +func (p *Page) Num() int { + return p.num +} + +func (p *Page) IsCurrent() bool { + return p.isCurrent +} + +func getMiddleIdx(numPages int) int { + if numPages%2 == 0 { + return numPages / 2 + } + return numPages/2 + 1 +} + +// Pages returns a list of nearby page numbers relative to current page. +// If value is -1 means "..." that more pages are not showing. +func (p *Paginater) Pages() []*Page { + if p.numPages == 0 { + return []*Page{} + } else if p.numPages == 1 && p.TotalPages() == 1 { + // Only show current page. + return []*Page{{1, true}} + } + + // Total page number is less or equal. + if p.TotalPages() <= p.numPages { + pages := make([]*Page, p.TotalPages()) + for i := range pages { + pages[i] = &Page{i + 1, i+1 == p.current} + } + return pages + } + + numPages := p.numPages + maxIdx := numPages - 1 + offsetIdx := 0 + hasMoreNext := false + + // Check more previous and next pages. + previousNum := getMiddleIdx(p.numPages) - 1 + if previousNum > p.current-1 { + previousNum -= previousNum - (p.current - 1) + } + nextNum := p.numPages - previousNum - 1 + if p.current+nextNum > p.TotalPages() { + delta := nextNum - (p.TotalPages() - p.current) + nextNum -= delta + previousNum += delta + } + + offsetVal := p.current - previousNum + if offsetVal > 1 { + numPages++ + maxIdx++ + offsetIdx = 1 + } + + if p.current+nextNum < p.TotalPages() { + numPages++ + hasMoreNext = true + } + + pages := make([]*Page, numPages) + + // There are more previous pages. + if offsetIdx == 1 { + pages[0] = &Page{-1, false} + } + // There are more next pages. + if hasMoreNext { + pages[len(pages)-1] = &Page{-1, false} + } + + // Check previous pages. + for i := 0; i < previousNum; i++ { + pages[offsetIdx+i] = &Page{i + offsetVal, false} + } + + pages[offsetIdx+previousNum] = &Page{p.current, true} + + // Check next pages. + for i := 1; i <= nextNum; i++ { + pages[offsetIdx+previousNum+i] = &Page{p.current + i, false} + } + + return pages +} diff --git a/vendor/vendor.json b/vendor/vendor.json index 48c5da5..ad5c90b 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -14,6 +14,12 @@ "revision": "0db4a625e949e956314d7d1adea9bf82384cc10c", "revisionTime": "2017-02-13T07:20:14Z" }, + { + "checksumSHA1": "TZJb+QbOuWcbYb30BriioMAjg/o=", + "path": "github.com/Unknwon/paginater", + "revision": "45e5d631308ea359946e761484147982c978d0df", + "revisionTime": "2017-04-05T23:39:47Z" + }, { "checksumSHA1": "1bK29RcAjCAMCYS2HS3O18w4m8k=", "path": "github.com/cihub/seelog",