#2052 advanced select ops for system notices
This commit is contained in:
parent
e82ee40e9e
commit
f41360d864
13 changed files with 186 additions and 54 deletions
|
@ -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>
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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>
|
||||||
|
|
2
gogs.go
2
gogs.go
|
@ -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())
|
||||||
|
|
|
@ -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
|
@ -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,
|
||||||
|
|
|
@ -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({
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
0.7.30.1204 Beta
|
0.7.31.1205 Beta
|
|
@ -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" .}}
|
||||||
|
|
Reference in a new issue