#2052 advanced select ops for system notices

This commit is contained in:
Unknwon 2015-12-05 01:09:14 -05:00
parent e82ee40e9e
commit f41360d864
13 changed files with 186 additions and 54 deletions

View file

@ -5,7 +5,7 @@ Gogs - Go Git Service [![Build Status](https://travis-ci.org/gogits/gogs.svg?bra
![](public/img/gogs-large-resize.png) ![](public/img/gogs-large-resize.png)
##### Current version: 0.7.30 Beta ##### Current version: 0.7.31 Beta
<table> <table>
<tr> <tr>

View file

@ -280,7 +280,7 @@ func runWeb(ctx *cli.Context) {
m.Group("/notices", func() { m.Group("/notices", func() {
m.Get("", admin.Notices) m.Get("", admin.Notices)
m.Get("/:id:int/delete", admin.DeleteNotice) m.Post("/delete", admin.DeleteNotices)
m.Get("/empty", admin.EmptyNotices) m.Get("/empty", admin.EmptyNotices)
}) })
}, adminReq) }, adminReq)

View file

@ -32,7 +32,7 @@ USER_PAGING_NUM = 50
; Number of repos that are showed in one page ; Number of repos that are showed in one page
REPO_PAGING_NUM = 50 REPO_PAGING_NUM = 50
; Number of notices that are showed in one page ; Number of notices that are showed in one page
NOTICE_PAGING_NUM = 50 NOTICE_PAGING_NUM = 25
; Number of organization that are showed in one page ; Number of organization that are showed in one page
ORG_PAGING_NUM = 50 ORG_PAGING_NUM = 50

View file

@ -991,12 +991,18 @@ monitor.start = Start Time
monitor.execute_time = Execution Time monitor.execute_time = Execution Time
notices.system_notice_list = System Notices notices.system_notice_list = System Notices
notices.empty_all = Remove All Notices notices.view_detail_header = View Notice Detail
notices.actions = Actions
notices.select_all = Select All
notices.deselect_all = Deselect All
notices.inverse_selection = Inverse Selection
notices.delete_selected = Delete Selected
notices.delete_all = Delete All Notices
notices.type = Type notices.type = Type
notices.type_1 = Repository notices.type_1 = Repository
notices.desc = Description notices.desc = Description
notices.op = Op. notices.op = Op.
notices.delete_success = System notice has been deleted successfully. notices.delete_success = System notices have been deleted successfully.
[action] [action]
create_repo = created repository <a href="%s">%s</a> create_repo = created repository <a href="%s">%s</a>

View file

@ -17,7 +17,7 @@ import (
"github.com/gogits/gogs/modules/setting" "github.com/gogits/gogs/modules/setting"
) )
const APP_VER = "0.7.30.1204 Beta" const APP_VER = "0.7.31.1205 Beta"
func init() { func init() {
runtime.GOMAXPROCS(runtime.NumCPU()) runtime.GOMAXPROCS(runtime.NumCPU())

View file

@ -5,9 +5,12 @@
package models package models
import ( import (
"strings"
"time" "time"
"github.com/Unknwon/com" "github.com/Unknwon/com"
"github.com/gogits/gogs/modules/base"
) )
type NoticeType int type NoticeType int
@ -18,7 +21,7 @@ const (
// Notice represents a system notice for admin. // Notice represents a system notice for admin.
type Notice struct { type Notice struct {
Id int64 ID int64 `xorm:"pk autoincr"`
Type NoticeType Type NoticeType
Description string `xorm:"TEXT"` Description string `xorm:"TEXT"`
Created time.Time `xorm:"CREATED"` Created time.Time `xorm:"CREATED"`
@ -71,3 +74,12 @@ func DeleteNotices(start, end int64) error {
_, err := sess.Delete(new(Notice)) _, err := sess.Delete(new(Notice))
return err return err
} }
// DeleteNoticesByIDs deletes notices by given IDs.
func DeleteNoticesByIDs(ids []int64) error {
if len(ids) == 0 {
return nil
}
_, err := x.Where("id IN (" + strings.Join(base.Int64sToStrings(ids), ",") + ")").Delete(new(Notice))
return err
}

File diff suppressed because one or more lines are too long

View file

@ -2999,12 +2999,18 @@ footer .container .links > *:first-child {
padding: 0; padding: 0;
font-size: 13px; font-size: 13px;
} }
.admin .table.segment:not(.striped) {
padding-top: 5px;
}
.admin .table.segment:not(.striped) thead th:last-child {
padding-right: 5px !important;
}
.admin .table.segment th { .admin .table.segment th {
padding-top: 5px; padding-top: 5px;
padding-bottom: 5px; padding-bottom: 5px;
} }
.admin .table.segment th:first-of-type, .admin .table.segment:not(.select) th:first-of-type,
.admin .table.segment td:first-of-type { .admin .table.segment:not(.select) td:first-of-type {
padding-left: 15px !important; padding-left: 15px !important;
} }
.admin .ui.header, .admin .ui.header,

View file

@ -319,23 +319,23 @@ function initRepository() {
$('#edit-title').click(editTitleToggle); $('#edit-title').click(editTitleToggle);
$('#cancel-edit-title').click(editTitleToggle); $('#cancel-edit-title').click(editTitleToggle);
$('#save-edit-title').click(editTitleToggle). $('#save-edit-title').click(editTitleToggle).
click(function () { click(function () {
if ($edit_input.val().length == 0 || if ($edit_input.val().length == 0 ||
$edit_input.val() == $issue_title.text()) { $edit_input.val() == $issue_title.text()) {
$edit_input.val($issue_title.text()); $edit_input.val($issue_title.text());
return false;
}
$.post($(this).data('update-url'), {
"_csrf": csrf,
"title": $edit_input.val()
},
function (data) {
$edit_input.val(data.title);
$issue_title.text(data.title);
});
return false; return false;
}); }
$.post($(this).data('update-url'), {
"_csrf": csrf,
"title": $edit_input.val()
},
function (data) {
$edit_input.val(data.title);
$issue_title.text(data.title);
});
return false;
});
// Edit issue or comment content // Edit issue or comment content
$('.edit-content').click(function () { $('.edit-content').click(function () {
@ -607,6 +607,50 @@ function initAdmin() {
} }
}); });
} }
// Notice
if ($('.admin.notice')) {
var $detail_modal = $('#detail-modal');
// Attach view detail modals
$('.view-detail').click(function () {
$detail_modal.find('.content p').text($(this).data('content'));
$detail_modal.modal('show');
return false;
});
// Select actions
var $checkboxes = $('.select.table .ui.checkbox');
$('.select.action').click(function () {
switch ($(this).data('action')) {
case 'select-all':
$checkboxes.checkbox('check');
break;
case 'deselect-all':
$checkboxes.checkbox('uncheck');
break;
case 'inverse':
$checkboxes.checkbox('toggle');
break;
}
});
$('#delete-selection').click(function () {
var $this = $(this);
$this.addClass("loading disabled");
var ids = [];
$checkboxes.each(function () {
if ($(this).checkbox('is checked')) {
ids.push($(this).data('id'));
}
});
$.post($this.data('link'), {
"_csrf": csrf,
"ids": ids
}).done(function () {
window.location.href = $this.data('redirect');
});
});
}
} }
function buttonsClickOnEnter() { function buttonsClickOnEnter() {
@ -734,9 +778,9 @@ $(document).ready(function () {
// Show exact time // Show exact time
$('.time-since').each(function () { $('.time-since').each(function () {
$(this).addClass('poping up'). $(this).addClass('poping up').
attr('data-content', $(this).attr('title')). attr('data-content', $(this).attr('title')).
attr('data-variation', 'inverted tiny'). attr('data-variation', 'inverted tiny').
attr('title', ''); attr('title', '');
}); });
// Semantic UI modules. // Semantic UI modules.
@ -750,6 +794,9 @@ $(document).ready(function () {
$('.slide.up.dropdown').dropdown({ $('.slide.up.dropdown').dropdown({
transition: 'slide up' transition: 'slide up'
}); });
$('.upward.dropdown').dropdown({
direction: 'upward'
});
$('.ui.accordion').accordion(); $('.ui.accordion').accordion();
$('.ui.checkbox').checkbox(); $('.ui.checkbox').checkbox();
$('.ui.progress').progress({ $('.ui.progress').progress({

View file

@ -5,13 +5,27 @@
.table.segment { .table.segment {
padding: 0; padding: 0;
font-size: 13px; font-size: 13px;
&:not(.striped) {
padding-top: 5px;
thead {
th:last-child {
padding-right: 5px !important;
}
}
}
th { th {
padding-top: 5px; padding-top: 5px;
padding-bottom: 5px; padding-bottom: 5px;
} }
th, td {
&:first-of-type { &:not(.select) {
padding-left: 15px !important; th, td {
&:first-of-type {
padding-left: 15px !important;
}
} }
} }
} }

View file

@ -5,6 +5,7 @@
package admin package admin
import ( import (
"github.com/Unknwon/com"
"github.com/Unknwon/paginater" "github.com/Unknwon/paginater"
"github.com/gogits/gogs/models" "github.com/gogits/gogs/models"
@ -41,15 +42,23 @@ func Notices(ctx *middleware.Context) {
ctx.HTML(200, NOTICES) ctx.HTML(200, NOTICES)
} }
func DeleteNotice(ctx *middleware.Context) { func DeleteNotices(ctx *middleware.Context) {
id := ctx.ParamsInt64(":id") strs := ctx.QueryStrings("ids[]")
if err := models.DeleteNotice(id); err != nil { ids := make([]int64, 0, len(strs))
ctx.Handle(500, "DeleteNotice", err) for i := range strs {
return id := com.StrTo(strs[i]).MustInt64()
if id > 0 {
ids = append(ids, id)
}
}
if err := models.DeleteNoticesByIDs(ids); err != nil {
ctx.Flash.Error("DeleteNoticesByIDs: " + err.Error())
ctx.Status(500)
} else {
ctx.Flash.Success(ctx.Tr("admin.notices.delete_success"))
ctx.Status(200)
} }
log.Trace("System notice deleted by admin (%s): %d", ctx.User.Name, id)
ctx.Flash.Success(ctx.Tr("admin.notices.delete_success"))
ctx.Redirect(setting.AppSubUrl + "/admin/notices")
} }
func EmptyNotices(ctx *middleware.Context) { func EmptyNotices(ctx *middleware.Context) {

View file

@ -1 +1 @@
0.7.30.1204 Beta 0.7.31.1205 Beta

View file

@ -1,5 +1,5 @@
{{template "base/head" .}} {{template "base/head" .}}
<div class="admin user"> <div class="admin notice">
<div class="ui container"> <div class="ui container">
<div class="ui grid"> <div class="ui grid">
{{template "admin/navbar" .}} {{template "admin/navbar" .}}
@ -7,32 +7,62 @@
{{template "base/alert" .}} {{template "base/alert" .}}
<h4 class="ui top attached header"> <h4 class="ui top attached header">
{{.i18n.Tr "admin.notices.system_notice_list"}} ({{.i18n.Tr "admin.total" .Total}}) {{.i18n.Tr "admin.notices.system_notice_list"}} ({{.i18n.Tr "admin.total" .Total}})
<div class="ui right">
<a class="ui red tiny button" href="{{AppSubUrl}}/admin/notices/empty">{{.i18n.Tr "admin.notices.empty_all"}}</a>
</div>
</h4> </h4>
<div class="ui attached table segment"> <div class="ui attached table segment">
<table class="ui very basic striped table"> <table class="ui very basic select selectable table">
<thead> <thead>
<tr> <tr>
<th></th>
<th>ID</th> <th>ID</th>
<th>{{.i18n.Tr "admin.notices.type"}}</th> <th>{{.i18n.Tr "admin.notices.type"}}</th>
<th>{{.i18n.Tr "admin.notices.desc"}}</th> <th>{{.i18n.Tr "admin.notices.desc"}}</th>
<th>{{.i18n.Tr "admin.users.created"}}</th> <th width="100px">{{.i18n.Tr "admin.users.created"}}</th>
<th>{{.i18n.Tr "admin.notices.op"}}</th> <th>{{.i18n.Tr "admin.notices.op"}}</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{{range .Notices}} {{range .Notices}}
<tr> <tr>
<td>{{.Id}}</td> <td class="collapsing">
<div class="ui fitted checkbox" data-id="{{.ID}}">
<input type="checkbox"> <label></label>
</div>
</td>
<td>{{.ID}}</td>
<td>{{$.i18n.Tr .TrStr}}</td> <td>{{$.i18n.Tr .TrStr}}</td>
<td><span>{{.Description}}</span></td> <td>{{SubStr .Description 0 120}}...</td>
<td>{{.Created}}</td> <td><span class="poping up" data-content="{{.Created}}" data-variation="inverted tiny">{{DateFmtShort .Created}}</span></td>
<td><a href="{{AppSubUrl}}/admin/notices/{{.Id}}/delete"><i class="fa fa-trash-o text-red"></i></a></td> <td><a href="#"><i class="browser icon view-detail" data-content="{{.Description}}"></i></a></td>
</tr> </tr>
{{end}} {{end}}
</tbody> </tbody>
<tfoot class="full-width">
<tr>
<th></th>
<th colspan="5">
<div class="ui right">
<a class="ui red small button" href="{{AppSubUrl}}/admin/notices/empty">{{.i18n.Tr "admin.notices.delete_all"}}</a>
</div>
<div class="ui floating upward dropdown small button">
<span class="text">{{.i18n.Tr "admin.notices.actions"}}</span>
<div class="menu">
<div class="item select action" data-action="select-all">
{{.i18n.Tr "admin.notices.select_all"}}
</div>
<div class="item select action" data-action="deselect-all">
{{.i18n.Tr "admin.notices.deselect_all"}}
</div>
<div class="item select action" data-action="inverse">
{{.i18n.Tr "admin.notices.inverse_selection"}}
</div>
</div>
</div>
<div class="ui small teal button" id="delete-selection" data-link="{{.Link}}/delete" data-redirect="{{.Link}}?page={{.Page.Current}}">
{{.i18n.Tr "admin.notices.delete_selected"}}
</div>
</th>
</tr>
</tfoot>
</table> </table>
</div> </div>
@ -63,4 +93,12 @@
</div> </div>
</div> </div>
</div> </div>
<div class="ui modal" id="detail-modal">
<i class="close icon"></i>
<div class="header">{{$.i18n.Tr "admin.notices.view_detail_header"}}</div>
<div class="content">
<p></p>
</div>
</div>
{{template "base/footer" .}} {{template "base/footer" .}}