mirror of
https://github.com/discourse/discourse.git
synced 2025-06-06 03:06:53 +08:00
FEATURE: editable badge groups
This commit is contained in:
@ -7,6 +7,7 @@
|
|||||||
@module Discourse
|
@module Discourse
|
||||||
**/
|
**/
|
||||||
export default Ember.ArrayController.extend({
|
export default Ember.ArrayController.extend({
|
||||||
|
needs: ['modal'],
|
||||||
itemController: 'admin-badge',
|
itemController: 'admin-badge',
|
||||||
queryParams: ['badgeId'],
|
queryParams: ['badgeId'],
|
||||||
badgeId: Em.computed.alias('selectedId'),
|
badgeId: Em.computed.alias('selectedId'),
|
||||||
|
@ -0,0 +1,80 @@
|
|||||||
|
export default Ember.Controller.extend({
|
||||||
|
needs: ['modal'],
|
||||||
|
|
||||||
|
modelChanged: function(){
|
||||||
|
|
||||||
|
var grouping = Em.Object.extend({});
|
||||||
|
|
||||||
|
var model = this.get('model');
|
||||||
|
var copy = Em.A();
|
||||||
|
|
||||||
|
if(model){
|
||||||
|
model.forEach(function(o){
|
||||||
|
copy.pushObject(grouping.create(o));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.set('workingCopy', copy);
|
||||||
|
}.observes('model'),
|
||||||
|
|
||||||
|
moveItem: function(item, delta){
|
||||||
|
var copy = this.get('workingCopy');
|
||||||
|
var index = copy.indexOf(item);
|
||||||
|
if (index + delta < 0 || index + delta >= copy.length){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
copy.removeAt(index);
|
||||||
|
copy.insertAt(index+delta, item);
|
||||||
|
},
|
||||||
|
|
||||||
|
actions: {
|
||||||
|
up: function(item){
|
||||||
|
this.moveItem(item, -1);
|
||||||
|
},
|
||||||
|
down: function(item){
|
||||||
|
this.moveItem(item, 1);
|
||||||
|
},
|
||||||
|
"delete": function(item){
|
||||||
|
this.get('workingCopy').removeObject(item);
|
||||||
|
},
|
||||||
|
cancel: function(){
|
||||||
|
this.set('model', null);
|
||||||
|
this.set('workingCopy', null);
|
||||||
|
this.send('closeModal');
|
||||||
|
},
|
||||||
|
edit: function(item){
|
||||||
|
item.set("editing", true);
|
||||||
|
},
|
||||||
|
save: function(item){
|
||||||
|
item.set("editing", false);
|
||||||
|
},
|
||||||
|
add: function(){
|
||||||
|
var obj = Em.Object.create({editing: true, name: "Enter Name"});
|
||||||
|
this.get('workingCopy').pushObject(obj);
|
||||||
|
},
|
||||||
|
saveAll: function(){
|
||||||
|
var self = this;
|
||||||
|
var items = this.get('workingCopy');
|
||||||
|
var groupIds = items.map(function(i){return i.get("id") || -1});
|
||||||
|
var names = items.map(function(i){return i.get("name")});
|
||||||
|
|
||||||
|
Discourse.ajax('/admin/badges/badge_groupings',{
|
||||||
|
data: {ids: groupIds, names: names},
|
||||||
|
method: 'POST'
|
||||||
|
}).then(function(data){
|
||||||
|
items = self.get("model");
|
||||||
|
items.clear();
|
||||||
|
data.badge_groupings.forEach(function(g){
|
||||||
|
items.pushObject(Em.Object.create(g));
|
||||||
|
});
|
||||||
|
self.set('model', null);
|
||||||
|
self.set('workingCopy', null);
|
||||||
|
self.send('closeModal');
|
||||||
|
},function(){
|
||||||
|
// TODO we can do better
|
||||||
|
bootbox.alert("Something went wrong");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
@ -1,7 +1,8 @@
|
|||||||
Discourse.AdminBadgesRoute = Discourse.Route.extend({
|
Discourse.AdminBadgesRoute = Discourse.Route.extend({
|
||||||
setupController: function(controller) {
|
setupController: function(controller) {
|
||||||
Discourse.ajax('/admin/badges.json').then(function(json){
|
Discourse.ajax('/admin/badges.json').then(function(json){
|
||||||
controller.set('badgeGroupings', json.badge_groupings);
|
|
||||||
|
controller.set('badgeGroupings', Em.A(json.badge_groupings));
|
||||||
controller.set('badgeTypes', json.badge_types);
|
controller.set('badgeTypes', json.badge_types);
|
||||||
controller.set('protectedSystemFields', json.admin_badges.protected_system_fields);
|
controller.set('protectedSystemFields', json.admin_badges.protected_system_fields);
|
||||||
var triggers = [];
|
var triggers = [];
|
||||||
@ -11,6 +12,12 @@ Discourse.AdminBadgesRoute = Discourse.Route.extend({
|
|||||||
controller.set('badgeTriggers', triggers);
|
controller.set('badgeTriggers', triggers);
|
||||||
controller.set('model', Discourse.Badge.createFromJson(json));
|
controller.set('model', Discourse.Badge.createFromJson(json));
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
actions: {
|
||||||
|
editGroupings: function(model){
|
||||||
|
Discourse.Route.showModal(this, 'admin_edit_badge_groupings', model);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -53,6 +53,7 @@
|
|||||||
content=controller.badgeGroupings
|
content=controller.badgeGroupings
|
||||||
optionValuePath="content.id"
|
optionValuePath="content.id"
|
||||||
optionLabelPath="content.name"}}
|
optionLabelPath="content.name"}}
|
||||||
|
<button {{action editGroupings controller.badgeGroupings}}><i class="fa fa-pencil"></i></button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
@ -0,0 +1,28 @@
|
|||||||
|
|
||||||
|
<div class="modal-body">
|
||||||
|
<div>
|
||||||
|
<ul class='badge-groupings'>
|
||||||
|
{{#each workingCopy}}
|
||||||
|
<li>
|
||||||
|
{{#if editing}}
|
||||||
|
{{input value=this.name}}
|
||||||
|
<button {{action save this}}><i class="fa fa-check"></i></button>
|
||||||
|
{{else}}
|
||||||
|
{{this.name}}
|
||||||
|
{{/if}}
|
||||||
|
<div class='actions'>
|
||||||
|
<button {{action edit this}}><i class="fa fa-pencil"></i></button>
|
||||||
|
<button {{action up this}}><i class="fa fa-toggle-up"></i></button>
|
||||||
|
<button {{action down this}}><i class="fa fa-toggle-down"></i></button>
|
||||||
|
<button {{action delete this}}><i class="fa fa-times"></i></button>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
{{/each}}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<button class='btn' {{action add}}>{{i18n admin.badges.new}}</button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button class='btn btn-primary' {{action saveAll}} {{bind-attr disabled="submitDisabled"}}>{{i18n admin.badges.save}}</button>
|
||||||
|
<a {{action cancel}}>{{i18n cancel}}</a>
|
||||||
|
</div>
|
@ -0,0 +1,5 @@
|
|||||||
|
|
||||||
|
Discourse.AdminEditBadgeGroupingsView = Discourse.ModalBodyView.extend({
|
||||||
|
templateName: 'admin/templates/modal/admin_edit_badge_groupings',
|
||||||
|
title: I18n.t('admin.badges.badge_groupings.modal_title')
|
||||||
|
});
|
@ -1205,3 +1205,21 @@ and (max-width : 500px) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.badge-groupings {
|
||||||
|
list-style: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 10px 3px;
|
||||||
|
li {
|
||||||
|
padding: 6px 0;
|
||||||
|
width: 600px;
|
||||||
|
border-bottom: 1px solid #dfdfdf;
|
||||||
|
}
|
||||||
|
.actions {
|
||||||
|
font-size: 17px;
|
||||||
|
float: right;
|
||||||
|
a {
|
||||||
|
margin-left: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -2,8 +2,8 @@ class Admin::BadgesController < Admin::AdminController
|
|||||||
|
|
||||||
def index
|
def index
|
||||||
data = {
|
data = {
|
||||||
badge_types: BadgeType.all.to_a,
|
badge_types: BadgeType.all.order(:id).to_a,
|
||||||
badge_groupings: BadgeGrouping.all.to_a,
|
badge_groupings: BadgeGrouping.all.order(:position).to_a,
|
||||||
badges: Badge.all.to_a,
|
badges: Badge.all.to_a,
|
||||||
protected_system_fields: Badge.protected_system_fields,
|
protected_system_fields: Badge.protected_system_fields,
|
||||||
triggers: Badge.trigger_hash
|
triggers: Badge.trigger_hash
|
||||||
@ -20,8 +20,24 @@ class Admin::BadgesController < Admin::AdminController
|
|||||||
render_serialized(badge_types, BadgeTypeSerializer, root: "badge_types")
|
render_serialized(badge_types, BadgeTypeSerializer, root: "badge_types")
|
||||||
end
|
end
|
||||||
|
|
||||||
def badge_groupings
|
def save_badge_groupings
|
||||||
badge_groupings = BadgeGrouping.all.to_a
|
|
||||||
|
badge_groupings = BadgeGrouping.all.order(:position).to_a
|
||||||
|
ids = params[:ids].map(&:to_i)
|
||||||
|
|
||||||
|
params[:names].each_with_index do |name,index|
|
||||||
|
id = ids[index].to_i
|
||||||
|
group = badge_groupings.find{|b| b.id == id} || BadgeGrouping.new()
|
||||||
|
group.name = name
|
||||||
|
group.position = index
|
||||||
|
group.save
|
||||||
|
end
|
||||||
|
|
||||||
|
badge_groupings.each do |g|
|
||||||
|
g.destroy unless g.system? || ids.include?(g.id)
|
||||||
|
end
|
||||||
|
|
||||||
|
badge_groupings = BadgeGrouping.all.order(:position).to_a
|
||||||
render_serialized(badge_groupings, BadgeGroupingSerializer, root: "badge_groupings")
|
render_serialized(badge_groupings, BadgeGroupingSerializer, root: "badge_groupings")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -8,6 +8,10 @@ class BadgeGrouping < ActiveRecord::Base
|
|||||||
|
|
||||||
has_many :badges
|
has_many :badges
|
||||||
|
|
||||||
|
def system?
|
||||||
|
id && id < 5
|
||||||
|
end
|
||||||
|
|
||||||
def default_position=(pos)
|
def default_position=(pos)
|
||||||
position ||= pos
|
position ||= pos
|
||||||
end
|
end
|
||||||
|
@ -1889,6 +1889,8 @@ en:
|
|||||||
description: Description
|
description: Description
|
||||||
badge_type: Badge Type
|
badge_type: Badge Type
|
||||||
badge_grouping: Group
|
badge_grouping: Group
|
||||||
|
badge_groupings:
|
||||||
|
modal_title: Badge Groupings
|
||||||
granted_by: Granted By
|
granted_by: Granted By
|
||||||
granted_at: Granted At
|
granted_at: Granted At
|
||||||
save: Save
|
save: Save
|
||||||
|
@ -146,7 +146,7 @@ Discourse::Application.routes.draw do
|
|||||||
resources :badges, constraints: AdminConstraint.new do
|
resources :badges, constraints: AdminConstraint.new do
|
||||||
collection do
|
collection do
|
||||||
get "types" => "badges#badge_types"
|
get "types" => "badges#badge_types"
|
||||||
get "groupings" => "badges#badge_groupings"
|
post "badge_groupings" => "badges#save_badge_groupings"
|
||||||
post "preview" => "badges#preview"
|
post "preview" => "badges#preview"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -1,14 +1,35 @@
|
|||||||
require 'spec_helper'
|
require 'spec_helper'
|
||||||
|
|
||||||
describe Admin::BadgesController do
|
describe Admin::BadgesController do
|
||||||
it "is a subclass of AdminController" do
|
|
||||||
(Admin::BadgesController < Admin::AdminController).should be_true
|
|
||||||
end
|
|
||||||
|
|
||||||
context "while logged in as an admin" do
|
context "while logged in as an admin" do
|
||||||
let!(:user) { log_in(:admin) }
|
let!(:user) { log_in(:admin) }
|
||||||
let!(:badge) { Fabricate(:badge) }
|
let!(:badge) { Fabricate(:badge) }
|
||||||
|
|
||||||
|
context '.save_badge_groupings' do
|
||||||
|
|
||||||
|
it 'can save badge groupings' do
|
||||||
|
groupings = BadgeGrouping.all.order(:position).to_a
|
||||||
|
groupings << BadgeGrouping.new(name: 'Test 1')
|
||||||
|
groupings << BadgeGrouping.new(name: 'Test 2')
|
||||||
|
|
||||||
|
groupings.shuffle!
|
||||||
|
|
||||||
|
names = groupings.map{|g| g.name}
|
||||||
|
ids = groupings.map{|g| g.id.to_s}
|
||||||
|
|
||||||
|
|
||||||
|
xhr :post, :save_badge_groupings, ids: ids, names: names
|
||||||
|
|
||||||
|
groupings2 = BadgeGrouping.all.order(:position).to_a
|
||||||
|
|
||||||
|
groupings2.map{|g| g.name}.should == names
|
||||||
|
(groupings.map(&:id) - groupings2.map{|g| g.id}).compact.should be_blank
|
||||||
|
|
||||||
|
::JSON.parse(response.body)["badge_groupings"].length.should == groupings2.length
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
context '.badge_types' do
|
context '.badge_types' do
|
||||||
it 'returns success' do
|
it 'returns success' do
|
||||||
xhr :get, :badge_types
|
xhr :get, :badge_types
|
||||||
|
Reference in New Issue
Block a user