#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)
##### Current version: 0.7.30 Beta
##### Current version: 0.7.31 Beta
<table>
<tr>

View file

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

View file

@ -32,7 +32,7 @@ USER_PAGING_NUM = 50
; Number of repos that are showed in one page
REPO_PAGING_NUM = 50
; 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
ORG_PAGING_NUM = 50

View file

@ -991,12 +991,18 @@ monitor.start = Start Time
monitor.execute_time = Execution Time
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_1 = Repository
notices.desc = Description
notices.op = Op.
notices.delete_success = System notice has been deleted successfully.
notices.delete_success = System notices have been deleted successfully.
[action]
create_repo = created repository <a href="%s">%s</a>

View file

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

View file

@ -5,9 +5,12 @@
package models
import (
"strings"
"time"
"github.com/Unknwon/com"
"github.com/gogits/gogs/modules/base"
)
type NoticeType int
@ -18,7 +21,7 @@ const (
// Notice represents a system notice for admin.
type Notice struct {
Id int64
ID int64 `xorm:"pk autoincr"`
Type NoticeType
Description string `xorm:"TEXT"`
Created time.Time `xorm:"CREATED"`
@ -71,3 +74,12 @@ func DeleteNotices(start, end int64) error {
_, err := sess.Delete(new(Notice))
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;
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 {
padding-top: 5px;
padding-bottom: 5px;
}
.admin .table.segment th:first-of-type,
.admin .table.segment td:first-of-type {
.admin .table.segment:not(.select) th:first-of-type,
.admin .table.segment:not(.select) td:first-of-type {
padding-left: 15px !important;
}
.admin .ui.header,

View file

@ -319,23 +319,23 @@ function initRepository() {
$('#edit-title').click(editTitleToggle);
$('#cancel-edit-title').click(editTitleToggle);
$('#save-edit-title').click(editTitleToggle).
click(function () {
if ($edit_input.val().length == 0 ||
$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);
});
click(function () {
if ($edit_input.val().length == 0 ||
$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;
});
// Edit issue or comment content
$('.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() {
@ -734,9 +778,9 @@ $(document).ready(function () {
// Show exact time
$('.time-since').each(function () {
$(this).addClass('poping up').
attr('data-content', $(this).attr('title')).
attr('data-variation', 'inverted tiny').
attr('title', '');
attr('data-content', $(this).attr('title')).
attr('data-variation', 'inverted tiny').
attr('title', '');
});
// Semantic UI modules.
@ -750,6 +794,9 @@ $(document).ready(function () {
$('.slide.up.dropdown').dropdown({
transition: 'slide up'
});
$('.upward.dropdown').dropdown({
direction: 'upward'
});
$('.ui.accordion').accordion();
$('.ui.checkbox').checkbox();
$('.ui.progress').progress({

View file

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

View file

@ -5,6 +5,7 @@
package admin
import (
"github.com/Unknwon/com"
"github.com/Unknwon/paginater"
"github.com/gogits/gogs/models"
@ -41,15 +42,23 @@ func Notices(ctx *middleware.Context) {
ctx.HTML(200, NOTICES)
}
func DeleteNotice(ctx *middleware.Context) {
id := ctx.ParamsInt64(":id")
if err := models.DeleteNotice(id); err != nil {
ctx.Handle(500, "DeleteNotice", err)
return
func DeleteNotices(ctx *middleware.Context) {
strs := ctx.QueryStrings("ids[]")
ids := make([]int64, 0, len(strs))
for i := range strs {
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) {

View file

@ -1 +1 @@
0.7.30.1204 Beta
0.7.31.1205 Beta

View file

@ -1,5 +1,5 @@
{{template "base/head" .}}
<div class="admin user">
<div class="admin notice">
<div class="ui container">
<div class="ui grid">
{{template "admin/navbar" .}}
@ -7,32 +7,62 @@
{{template "base/alert" .}}
<h4 class="ui top attached header">
{{.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>
<div class="ui attached table segment">
<table class="ui very basic striped table">
<table class="ui very basic select selectable table">
<thead>
<tr>
<th></th>
<th>ID</th>
<th>{{.i18n.Tr "admin.notices.type"}}</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>
</tr>
</thead>
<tbody>
{{range .Notices}}
<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><span>{{.Description}}</span></td>
<td>{{.Created}}</td>
<td><a href="{{AppSubUrl}}/admin/notices/{{.Id}}/delete"><i class="fa fa-trash-o text-red"></i></a></td>
<td>{{SubStr .Description 0 120}}...</td>
<td><span class="poping up" data-content="{{.Created}}" data-variation="inverted tiny">{{DateFmtShort .Created}}</span></td>
<td><a href="#"><i class="browser icon view-detail" data-content="{{.Description}}"></i></a></td>
</tr>
{{end}}
</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>
</div>
@ -63,4 +93,12 @@
</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" .}}