UI still a tad rough, but we have a first pass of secure categories

This commit is contained in:
Sam 2013-05-10 16:47:47 +10:00
parent 4030722a8f
commit 942f168ab6
16 changed files with 127 additions and 42 deletions

View file

@ -9,7 +9,7 @@
<li>{{#linkTo 'adminSiteContents'}}{{i18n admin.site_content.title}}{{/linkTo}}</li> <li>{{#linkTo 'adminSiteContents'}}{{i18n admin.site_content.title}}{{/linkTo}}</li>
{{/if}} {{/if}}
<li>{{#linkTo 'adminUsersList.active'}}{{i18n admin.users.title}}{{/linkTo}}</li> <li>{{#linkTo 'adminUsersList.active'}}{{i18n admin.users.title}}{{/linkTo}}</li>
<!--<li>{{#linkTo 'admin.groups'}}{{i18n admin.groups.title}}{{/linkTo}}</li>--> <li>{{#linkTo 'admin.groups'}}{{i18n admin.groups.title}}{{/linkTo}}</li>
<li>{{#linkTo 'admin.email_logs'}}{{i18n admin.email_logs.title}}{{/linkTo}}</li> <li>{{#linkTo 'admin.email_logs'}}{{i18n admin.email_logs.title}}{{/linkTo}}</li>
<li>{{#linkTo 'adminFlags.active'}}{{i18n admin.flags.title}}{{/linkTo}}</li> <li>{{#linkTo 'adminFlags.active'}}{{i18n admin.flags.title}}{{/linkTo}}</li>
{{#if Discourse.currentUser.admin}} {{#if Discourse.currentUser.admin}}

View file

@ -15,6 +15,9 @@ Discourse.Category = Discourse.Model.extend({
if (!this.get('color')) this.set('color', Discourse.SiteSettings.uncategorized_color); if (!this.get('color')) this.set('color', Discourse.SiteSettings.uncategorized_color);
if (!this.get('text_color')) this.set('text_color', Discourse.SiteSettings.uncategorized_text_color); if (!this.get('text_color')) this.set('text_color', Discourse.SiteSettings.uncategorized_text_color);
} }
this.set("availableGroups", Em.A(this.get("available_groups")));
this.set("groups", Em.A(this.groups));
}, },
url: function() { url: function() {
@ -40,7 +43,9 @@ Discourse.Category = Discourse.Model.extend({
name: this.get('name'), name: this.get('name'),
color: this.get('color'), color: this.get('color'),
text_color: this.get('text_color'), text_color: this.get('text_color'),
hotness: this.get('hotness') hotness: this.get('hotness'),
secure: this.get('secure'),
group_names: this.get('groups').join(",")
}, },
type: this.get('id') ? 'PUT' : 'POST' type: this.get('id') ? 'PUT' : 'POST'
}); });
@ -48,6 +53,17 @@ Discourse.Category = Discourse.Model.extend({
destroy: function(callback) { destroy: function(callback) {
return Discourse.ajax("/categories/" + (this.get('slug') || this.get('id')), { type: 'DELETE' }); return Discourse.ajax("/categories/" + (this.get('slug') || this.get('id')), { type: 'DELETE' });
},
addGroup: function(group){
this.get("groups").addObject(group);
this.get("availableGroups").removeObject(group);
},
removeGroup: function(group){
this.get("groups").removeObject(group);
this.get("availableGroups").addObject(group);
} }
}); });

View file

@ -23,10 +23,30 @@
{{/if}} {{/if}}
</section> </section>
<section class='field'>
<label>{{i18n category.security}}</label>
<label>
{{view Ember.Checkbox checkedBinding="secure"}}
{{i18n category.is_secure}}
</label>
{{#if secure}}
<div>
{{i18n category.allowed_groups}}
{{#each groups}}
{{this}} <a {{action removeGroup this target="view"}}>x</a>
{{/each}}
</div>
{{view Ember.Select contentBinding="availableGroups" valueBinding="view.selectedGroup"}}
<button {{action addGroup target="view"}}>{{i18n category.add_group}}</button>
{{/if}}
</section>
<!-- Sam, disabled for now
<section class='field'> <section class='field'>
<label>{{i18n category.hotness}}</label> <label>{{i18n category.hotness}}</label>
{{view Discourse.HotnessView hotnessBinding="hotness"}} {{view Discourse.HotnessView hotnessBinding="hotness"}}
</section> </section>
-->
{{/unless}} {{/unless}}
<section class='field'> <section class='field'>
@ -58,4 +78,4 @@
</div> </div>
</div> </div>
{{/with}} {{/with}}

View file

@ -93,6 +93,16 @@ Discourse.EditCategoryView = Discourse.ModalBodyView.extend({
return false; return false;
}, },
addGroup: function(){
this.get("category").addGroup(this.get("selectedGroup"));
},
removeGroup: function(group){
// OBVIOUS, Ember treats this as Ember.String, we need a real string here
group = group + "";
this.get("category").removeGroup(group);
},
saveCategory: function() { saveCategory: function() {
var categoryView = this; var categoryView = this;
this.set('saving', true); this.set('saving', true);

View file

@ -18,7 +18,7 @@
vertical-align: middle; vertical-align: middle;
padding-top: 4px; padding-top: 4px;
padding-left: 15px; padding-left: 15px;
max-width: 320px; max-width: 300px;
.colorpicker { .colorpicker {
border: 1px solid $darkish_gray; border: 1px solid $darkish_gray;

View file

@ -265,7 +265,6 @@ body {
} }
select, textarea { select, textarea {
display: inline-block; display: inline-block;
height: 18px;
padding: 4px; padding: 4px;
margin-bottom: 9px; margin-bottom: 9px;
font-size: 13px; font-size: 13px;

View file

@ -48,7 +48,7 @@ class CategoriesController < ApplicationController
end end
def category_param_keys def category_param_keys
[required_param_keys, :hotness].flatten! [required_param_keys, :hotness, :secure, :group_names].flatten!
end end
def category_params def category_params

View file

@ -92,7 +92,7 @@ class PostsController < ApplicationController
result = {post: post_serializer.as_json} result = {post: post_serializer.as_json}
if revisor.category_changed.present? if revisor.category_changed.present?
result[:category] = CategorySerializer.new(revisor.category_changed, scope: guardian, root: false).as_json result[:category] = BasicCategorySerializer.new(revisor.category_changed, scope: guardian, root: false).as_json
end end
render_json_dump(result) render_json_dump(result)

View file

@ -99,6 +99,15 @@ class Category < ActiveRecord::Base
self.secure self.secure
end end
def group_names=(names)
# this line bothers me, destroying in AR can not seem to be queued, thinking of extending it
category_groups.destroy_all unless new_record?
ids = Group.where(name: names.split(",")).select(:id).map(&:id)
ids.each do |id|
category_groups.build(group_id: id)
end
end
def deny(group) def deny(group)
if group == :all if group == :all
self.secure = true self.secure = true
@ -108,7 +117,8 @@ class Category < ActiveRecord::Base
def allow(group) def allow(group)
if group == :all if group == :all
self.secure = false self.secure = false
category_groups.clear # this is kind of annoying, there should be a clean way of queuing this stuff
category_groups.destroy_all unless new_record?
else else
groups.push(group) groups.push(group)
end end

View file

@ -9,7 +9,15 @@ class CategoryList
.includes(:featured_users) .includes(:featured_users)
.where('topics.visible' => true) .where('topics.visible' => true)
.order('categories.topics_week desc, categories.topics_month desc, categories.topics_year desc') .order('categories.topics_week desc, categories.topics_month desc, categories.topics_year desc')
.to_a
allowed_ids = current_user ? current_user.secure_category_ids : nil
if allowed_ids.present?
@categories = @categories.where("categories.secure = 'f' OR categories.id in (?)", allowed_ids)
else
@categories = @categories.where("categories.secure = 'f'")
end
@categories = @categories.to_a
# Support for uncategorized topics # Support for uncategorized topics
uncategorized_topics = Topic uncategorized_topics = Topic

View file

@ -564,7 +564,7 @@ class User < ActiveRecord::Base
end end
def secure_category_ids def secure_category_ids
cats = self.moderator? ? Category.select(:id).where(:secure, true) : secure_categories.select('categories.id') cats = self.staff? ? Category.select(:id).where(secure: true) : secure_categories.select('categories.id')
cats.map{|c| c.id} cats.map{|c| c.id}
end end

View file

@ -0,0 +1,13 @@
class BasicCategorySerializer < ApplicationSerializer
attributes :id,
:name,
:color,
:text_color,
:slug,
:topic_count,
:description,
:topic_url,
:hotness
end

View file

@ -1,13 +1,13 @@
class CategorySerializer < ApplicationSerializer class CategorySerializer < BasicCategorySerializer
attributes :id, attributes :secure, :groups, :available_groups
:name,
:color, def groups
:text_color, @groups ||= object.groups.order("name").all.map(&:name)
:slug, end
:topic_count,
:description, def available_groups
:topic_url, Group.order("name").map(&:name) - groups
:hotness end
end end

View file

@ -758,6 +758,11 @@ en:
change_in_category_topic: "Edit Description" change_in_category_topic: "Edit Description"
hotness: "Hotness" hotness: "Hotness"
already_used: 'This color has been used by another category' already_used: 'This color has been used by another category'
is_secure: "Secure category?"
add_group: "Add Group"
security: "Security"
allowed_groups: "Allowed Groups:"
flagging: flagging:
title: 'Why are you flagging this post?' title: 'Why are you flagging this post?'

View file

@ -16,23 +16,11 @@ describe CategoryList do
let!(:topic) { Fabricate(:topic)} let!(:topic) { Fabricate(:topic)}
let(:category) { category_list.categories.first } let(:category) { category_list.categories.first }
it "has a category" do it "has the right category" do
category.should be_present category.should be_present
end
it "has the uncategorized label" do
category.name.should == SiteSetting.uncategorized_name category.name.should == SiteSetting.uncategorized_name
end
it "has the uncategorized slug" do
category.slug.should == SiteSetting.uncategorized_name category.slug.should == SiteSetting.uncategorized_name
end
it "has one topic this week" do
category.topics_week.should == 1 category.topics_week.should == 1
end
it "contains the topic in featured_topics" do
category.featured_topics.should == [topic] category.featured_topics.should == [topic]
end end
@ -40,6 +28,23 @@ describe CategoryList do
end end
context "security" do
it "properly hide secure categories" do
admin = Fabricate(:admin)
user = Fabricate(:user)
cat = Fabricate(:category)
topic = Fabricate(:topic, category: cat)
cat.deny(:all)
cat.allow(Group[:admins])
cat.save
CategoryList.new(admin).categories.count.should == 1
CategoryList.new(user).categories.count.should == 0
CategoryList.new(nil).categories.count.should == 0
end
end
context "with a category" do context "with a category" do
let!(:topic_category) { Fabricate(:category) } let!(:topic_category) { Fabricate(:category) }

View file

@ -134,22 +134,21 @@ describe CategoriesController do
describe 'success' do describe 'success' do
before do before do
xhr :put, :update, id: @category.id, name: 'science', color: '000', text_color: '0ff' # might as well test this as well
@category.allow(Group[:admins])
@category.save
xhr :put, :update, id: @category.id, name: 'science', color: '000', text_color: '0ff', group_names: Group[:staff].name, secure: 'true'
@category.reload @category.reload
end end
it 'updates the name' do it 'updates the group correctly' do
@category.name.should == 'science' @category.name.should == 'science'
end
it 'updates the color' do
@category.color.should == '000' @category.color.should == '000'
end
it 'updates the text color' do
@category.text_color.should == '0ff' @category.text_color.should == '0ff'
@category.secure?.should be_true
@category.groups.count.should == 1
end end
end end
end end