mirror of
https://github.com/codeninjasllc/discourse.git
synced 2024-11-27 09:36:19 -05:00
work in progress, add fidelity to category group permissions (full, create posts, readonly)
This commit is contained in:
parent
8d947535a8
commit
ecf17cfebb
38 changed files with 349 additions and 126 deletions
|
@ -101,14 +101,12 @@ Discourse.EditCategoryController = Discourse.ObjectController.extend(Discourse.M
|
|||
return false;
|
||||
},
|
||||
|
||||
addGroup: function(){
|
||||
this.get('model').addGroup(this.get("selectedGroup"));
|
||||
addPermission: function(group, permission_id){
|
||||
this.get('model').addPermission({group_name: group + "", permission: Discourse.PermissionType.create({id: permission_id})});
|
||||
},
|
||||
|
||||
removeGroup: function(group){
|
||||
// OBVIOUS, Ember treats this as Ember.String, we need a real string here
|
||||
group = group + "";
|
||||
this.get('model').removeGroup(group);
|
||||
removePermission: function(permission){
|
||||
this.get('model').removePermission(permission);
|
||||
},
|
||||
|
||||
saveCategory: function() {
|
||||
|
|
|
@ -11,9 +11,21 @@ Discourse.Category = Discourse.Model.extend({
|
|||
init: function() {
|
||||
this._super();
|
||||
this.set("availableGroups", Em.A(this.get("available_groups")));
|
||||
this.set("groups", Em.A(this.groups));
|
||||
this.set("permissions", Em.A(_.map(this.group_permissions, function(elem){
|
||||
return {
|
||||
group_name: elem.group_name,
|
||||
permission: Discourse.PermissionType.create({id: elem.permission_type})
|
||||
};
|
||||
})));
|
||||
},
|
||||
|
||||
availablePermissions: function(){
|
||||
return [ Discourse.PermissionType.create({id: Discourse.PermissionType.FULL}),
|
||||
Discourse.PermissionType.create({id: Discourse.PermissionType.CREATE_POST}),
|
||||
Discourse.PermissionType.create({id: Discourse.PermissionType.READONLY})
|
||||
];
|
||||
}.property(),
|
||||
|
||||
searchContext: function() {
|
||||
return ({ type: 'category', id: this.get('id'), category: this });
|
||||
}.property('id'),
|
||||
|
@ -37,15 +49,20 @@ Discourse.Category = Discourse.Model.extend({
|
|||
}
|
||||
|
||||
return Discourse.ajax(url, {
|
||||
data: {
|
||||
contentType: 'application/json',
|
||||
dataType: 'json',
|
||||
data: JSON.stringify({
|
||||
name: this.get('name'),
|
||||
color: this.get('color'),
|
||||
text_color: this.get('text_color'),
|
||||
hotness: this.get('hotness'),
|
||||
secure: this.get('secure'),
|
||||
group_names: this.get('groups').join(","),
|
||||
permissions: _.map(
|
||||
this.get('permissions'), function(p){
|
||||
return { group_name: p.group_name, permission_type: p.permission.id};
|
||||
}),
|
||||
auto_close_days: this.get('auto_close_days')
|
||||
},
|
||||
}),
|
||||
type: this.get('id') ? 'PUT' : 'POST'
|
||||
});
|
||||
},
|
||||
|
@ -54,22 +71,30 @@ Discourse.Category = Discourse.Model.extend({
|
|||
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);
|
||||
addPermission: function(permission){
|
||||
this.get("permissions").addObject(permission);
|
||||
this.get("availableGroups").removeObject(permission.group_name);
|
||||
},
|
||||
|
||||
|
||||
removeGroup: function(group){
|
||||
this.get("groups").removeObject(group);
|
||||
this.get("availableGroups").addObject(group);
|
||||
removePermission: function(permission){
|
||||
this.get("permissions").removeObject(permission);
|
||||
this.get("availableGroups").addObject(permission.group_name);
|
||||
},
|
||||
|
||||
// note, this is used in a data attribute, data attributes get downcased
|
||||
// to avoid confusion later on using this naming here.
|
||||
description_text: function(){
|
||||
return $("<div>" + this.get("description") + "</div>").text();
|
||||
}.property("description")
|
||||
}.property("description"),
|
||||
|
||||
permissions: function(){
|
||||
return Em.A([
|
||||
{group_name: "everyone", permission: Discourse.PermissionType.create({id: 1})},
|
||||
{group_name: "admins", permission: Discourse.PermissionType.create({id: 2}) },
|
||||
{group_name: "crap", permission: Discourse.PermissionType.create({id: 3}) }
|
||||
]);
|
||||
}.property()
|
||||
|
||||
});
|
||||
|
||||
|
|
23
app/assets/javascripts/discourse/models/permission_type.js
Normal file
23
app/assets/javascripts/discourse/models/permission_type.js
Normal file
|
@ -0,0 +1,23 @@
|
|||
|
||||
Discourse.PermissionType = Discourse.Model.extend({
|
||||
description: function(){
|
||||
var key = "";
|
||||
|
||||
switch(this.get("id")){
|
||||
case 1:
|
||||
key = "full";
|
||||
break;
|
||||
case 2:
|
||||
key = "create_post";
|
||||
break;
|
||||
case 3:
|
||||
key = "readonly";
|
||||
break;
|
||||
}
|
||||
return I18n.t("permission_types." + key);
|
||||
}.property("id")
|
||||
});
|
||||
|
||||
Discourse.PermissionType.FULL = 1;
|
||||
Discourse.PermissionType.CREATE_POST = 2;
|
||||
Discourse.PermissionType.READONLY = 3;
|
|
@ -60,25 +60,18 @@
|
|||
{{#unless isUncategorized}}
|
||||
<div {{bindAttr class=":modal-tab :options-tab securitySelected::invisible"}}>
|
||||
<section class='field'>
|
||||
<label>
|
||||
{{input type="checkbox" checked=secure}}
|
||||
{{i18n category.is_secure}}
|
||||
</label>
|
||||
{{#if secure}}
|
||||
<div class="secure-category-options">
|
||||
<label>{{i18n category.allowed_groups}}</label>
|
||||
<ul class="badge-list">
|
||||
{{#each groups}}
|
||||
<li class="badge-group">
|
||||
{{this}}
|
||||
<a {{action removeGroup this}}><i class="icon icon-remove-sign"></i></a>
|
||||
</li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
{{view Ember.Select contentBinding="availableGroups" valueBinding="selectedGroup"}}
|
||||
<button {{action addGroup}} class="btn btn-small">{{i18n category.add_group}}</button>
|
||||
</div>
|
||||
{{/if}}
|
||||
<ul>
|
||||
{{#each permissions}}
|
||||
<li>
|
||||
<span class="badge-group">{{group_name}}</span>
|
||||
<span class="permission">{{permission.description}}</span>
|
||||
<a {{action removePermission this}}><i class="icon icon-remove-sign"></i></a>
|
||||
</li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
{{view Ember.Select contentBinding="availableGroups" valueBinding="selectedGroup"}}
|
||||
{{view Ember.Select optionValuePath="content.id" optionLabelPath="content.description" contentBinding="availablePermissions" valueBinding="selectedPermission"}}
|
||||
<button {{action addPermission selectedGroup selectedPermission}} class="btn btn-small">{{i18n category.add_group}}</button>
|
||||
</section>
|
||||
</div>
|
||||
<div {{bindAttr class=":modal-tab :options-tab settingsSelected::invisible"}}>
|
||||
|
|
|
@ -102,7 +102,7 @@ class ApplicationController < ActionController::Base
|
|||
guardian.current_user.sync_notification_channel_position
|
||||
end
|
||||
|
||||
store_preloaded("site", Site.cached_json(current_user))
|
||||
store_preloaded("site", Site.cached_json(guardian))
|
||||
|
||||
if current_user.present?
|
||||
store_preloaded("currentUser", MultiJson.dump(CurrentUserSerializer.new(current_user, root: false)))
|
||||
|
|
|
@ -53,7 +53,7 @@ class CategoriesController < ApplicationController
|
|||
end
|
||||
|
||||
def category_param_keys
|
||||
[required_param_keys, :hotness, :secure, :group_names, :auto_close_days].flatten!
|
||||
[required_param_keys, :hotness, :read_restricted, :permissions, :auto_close_days].flatten!
|
||||
end
|
||||
|
||||
def category_params
|
||||
|
|
|
@ -22,6 +22,7 @@ class Category < ActiveRecord::Base
|
|||
|
||||
before_validation :ensure_slug
|
||||
after_save :invalidate_site_cache
|
||||
before_save :apply_permissions
|
||||
after_create :create_category_definition
|
||||
after_create :publish_categories_list
|
||||
after_destroy :invalidate_site_cache
|
||||
|
@ -34,16 +35,49 @@ class Category < ActiveRecord::Base
|
|||
scope :secured, ->(guardian = nil) {
|
||||
ids = guardian.secure_category_ids if guardian
|
||||
if ids.present?
|
||||
where("NOT categories.secure or categories.id in (:cats)", cats: ids)
|
||||
where("NOT categories.read_restricted or categories.id in (:cats)", cats: ids)
|
||||
else
|
||||
where("NOT categories.secure")
|
||||
where("NOT categories.read_restricted")
|
||||
end
|
||||
}
|
||||
|
||||
scope :topic_create_allowed, ->(guardian) {
|
||||
scoped_to_permissions(guardian, [:full])
|
||||
}
|
||||
|
||||
scope :post_create_allowed, ->(guardian) {
|
||||
scoped_to_permissions(guardian, [:create_post, :full])
|
||||
}
|
||||
delegate :post_template, to: 'self.class'
|
||||
|
||||
attr_accessor :displayable_topics
|
||||
|
||||
|
||||
def self.scoped_to_permissions(guardian, permission_types)
|
||||
if guardian && guardian.is_staff?
|
||||
scoped
|
||||
else
|
||||
permission_types = permission_types.map{ |permission_type|
|
||||
CategoryGroup.permission_types[permission_type]
|
||||
}
|
||||
where("categories.id in (
|
||||
SELECT c.id FROM categories c
|
||||
WHERE (
|
||||
NOT c.read_restricted AND
|
||||
NOT EXISTS(
|
||||
SELECT 1 FROM category_groups cg WHERE cg.category_id = categories.id )
|
||||
) OR EXISTS(
|
||||
SELECT 1 FROM category_groups cg
|
||||
WHERE permission_type in (?) AND
|
||||
cg.category_id = categories.id AND
|
||||
group_id IN (
|
||||
SELECT g.group_id FROM group_users g where g.user_id = ?
|
||||
)
|
||||
)
|
||||
)", permission_types,(!guardian || guardian.user.blank?) ? -1 : guardian.user.id)
|
||||
end
|
||||
end
|
||||
|
||||
# Internal: Update category stats: # of topics in past year, month, week for
|
||||
# all categories.
|
||||
def self.update_stats
|
||||
|
@ -119,28 +153,65 @@ class Category < ActiveRecord::Base
|
|||
end
|
||||
end
|
||||
|
||||
def deny(group)
|
||||
if group == :all
|
||||
self.secure = true
|
||||
end
|
||||
# will reset permission on a topic to a particular
|
||||
# set.
|
||||
#
|
||||
# Available permissions are, :full, :create_post, :readonly
|
||||
# hash can be:
|
||||
#
|
||||
# :everyone => :full - everyone has everything
|
||||
# :everyone => :readonly, :staff => :full
|
||||
# 7 => 1 # you can pass a group_id and permission id
|
||||
def set_permissions(permissions)
|
||||
self.read_restricted, @permissions = Category.resolve_permissions(permissions)
|
||||
|
||||
# Ideally we can just call .clear here, but it runs SQL, we only want to run it
|
||||
# on save.
|
||||
end
|
||||
|
||||
def allow(group)
|
||||
if group == :all
|
||||
self.secure = false
|
||||
# this is kind of annoying, there should be a clean way of queuing this stuff
|
||||
category_groups.destroy_all unless new_record?
|
||||
else
|
||||
groups.push(group)
|
||||
def apply_permissions
|
||||
if @permissions
|
||||
category_groups.destroy_all
|
||||
@permissions.each do |group_id, permission_type|
|
||||
category_groups.build(group_id: group_id, permission_type: permission_type)
|
||||
end
|
||||
@permissions = nil
|
||||
end
|
||||
end
|
||||
|
||||
def secure_group_ids
|
||||
if self.secure
|
||||
if self.read_restricted?
|
||||
groups.pluck("groups.id")
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def self.resolve_permissions(permissions)
|
||||
read_restricted = true
|
||||
|
||||
everyone = Group::AUTO_GROUPS[:everyone]
|
||||
full = CategoryGroup.permission_types[:full]
|
||||
|
||||
mapped = permissions.map do |group,permission|
|
||||
group = group.id if Group === group
|
||||
|
||||
# subtle, using Group[] ensures the group exists in the DB
|
||||
group = Group[group].id unless Fixnum === group
|
||||
permission = CategoryGroup.permission_types[permission] unless Fixnum === permission
|
||||
|
||||
[group, permission]
|
||||
end
|
||||
|
||||
mapped.each do |group, permission|
|
||||
if group == everyone && permission == full
|
||||
return [false, []]
|
||||
end
|
||||
|
||||
read_restricted = false if group == everyone
|
||||
end
|
||||
|
||||
[read_restricted, mapped]
|
||||
end
|
||||
end
|
||||
|
||||
# == Schema Information
|
||||
|
@ -162,7 +233,7 @@ end
|
|||
# description :text
|
||||
# text_color :string(6) default("FFFFFF"), not null
|
||||
# hotness :float default(5.0), not null
|
||||
# secure :boolean default(FALSE), not null
|
||||
# read_restricted :boolean default(FALSE), not null
|
||||
# auto_close_days :float
|
||||
#
|
||||
# Indexes
|
||||
|
|
|
@ -1,16 +1,22 @@
|
|||
class CategoryGroup < ActiveRecord::Base
|
||||
belongs_to :category
|
||||
belongs_to :group
|
||||
|
||||
def self.permission_types
|
||||
@permission_types ||= Enum.new(:full, :create_post, :readonly)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# == Schema Information
|
||||
#
|
||||
# Table name: category_groups
|
||||
#
|
||||
# id :integer not null, primary key
|
||||
# category_id :integer not null
|
||||
# group_id :integer not null
|
||||
# created_at :datetime not null
|
||||
# updated_at :datetime not null
|
||||
# id :integer not null, primary key
|
||||
# category_id :integer not null
|
||||
# group_id :integer not null
|
||||
# created_at :datetime not null
|
||||
# updated_at :datetime not null
|
||||
# permission_type :integer default(1)
|
||||
#
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ class Group < ActiveRecord::Base
|
|||
validate :name_format_validator
|
||||
|
||||
AUTO_GROUPS = {
|
||||
:everyone => 0,
|
||||
:admins => 1,
|
||||
:moderators => 2,
|
||||
:staff => 3,
|
||||
|
@ -34,6 +35,10 @@ class Group < ActiveRecord::Base
|
|||
group.save!
|
||||
end
|
||||
|
||||
# the everyone group is special, it can include non-users so there is no
|
||||
# way to have the membership in a table
|
||||
return group if name == :everyone
|
||||
|
||||
group.name = I18n.t("groups.default_names.#{name}")
|
||||
|
||||
# don't allow shoddy localization to break this
|
||||
|
|
|
@ -59,6 +59,7 @@ end
|
|||
# created_at :datetime not null
|
||||
# updated_at :datetime not null
|
||||
# deleted_at :datetime
|
||||
# deleted_by_id :integer
|
||||
#
|
||||
# Indexes
|
||||
#
|
||||
|
|
|
@ -412,6 +412,8 @@ end
|
|||
# percent_rank :float default(1.0)
|
||||
# notify_user_count :integer default(0), not null
|
||||
# like_score :integer default(0), not null
|
||||
# deleted_by_id :integer
|
||||
# nuked_user :boolean default(FALSE)
|
||||
#
|
||||
# Indexes
|
||||
#
|
||||
|
|
|
@ -27,7 +27,7 @@ class Site
|
|||
|
||||
def categories
|
||||
Category
|
||||
.secured(@guardian)
|
||||
.topic_create_allowed(@guardian)
|
||||
.latest
|
||||
.includes(:topic_only_relative_url)
|
||||
end
|
||||
|
|
|
@ -103,9 +103,9 @@ class Topic < ActiveRecord::Base
|
|||
# Query conditions
|
||||
condition =
|
||||
if ids.present?
|
||||
["NOT c.secure or c.id in (:cats)", cats: ids]
|
||||
["NOT c.read_restricted or c.id in (:cats)", cats: ids]
|
||||
else
|
||||
["NOT c.secure"]
|
||||
["NOT c.read_restricted"]
|
||||
end
|
||||
|
||||
where("category_id IS NULL OR category_id IN (
|
||||
|
@ -629,8 +629,8 @@ class Topic < ActiveRecord::Base
|
|||
self
|
||||
end
|
||||
|
||||
def secure_category?
|
||||
category && category.secure
|
||||
def read_restricted_category?
|
||||
category && category.read_restricted
|
||||
end
|
||||
|
||||
private
|
||||
|
@ -692,6 +692,7 @@ end
|
|||
# auto_close_at :datetime
|
||||
# auto_close_user_id :integer
|
||||
# auto_close_started_at :datetime
|
||||
# deleted_by_id :integer
|
||||
#
|
||||
# Indexes
|
||||
#
|
||||
|
|
|
@ -113,11 +113,11 @@ class TopicTrackingState
|
|||
((#{unread}) OR (#{new})) AND
|
||||
(topics.visible OR u.admin OR u.moderator) AND
|
||||
topics.deleted_at IS NULL AND
|
||||
( category_id IS NULL OR NOT c.secure OR category_id IN (
|
||||
( category_id IS NULL OR NOT c.read_restricted OR category_id IN (
|
||||
SELECT c2.id FROM categories c2
|
||||
JOIN category_groups cg ON cg.category_id = c2.id
|
||||
JOIN group_users gu ON gu.user_id = u.id AND cg.group_id = gu.group_id
|
||||
WHERE c2.secure )
|
||||
WHERE c2.read_restricted )
|
||||
)
|
||||
|
||||
SQL
|
||||
|
|
|
@ -486,10 +486,14 @@ class User < ActiveRecord::Base
|
|||
end
|
||||
|
||||
def secure_category_ids
|
||||
cats = self.staff? ? Category.select(:id).where(secure: true) : secure_categories.select('categories.id')
|
||||
cats = self.staff? ? Category.select(:id).where(read_restricted: true) : secure_categories.select('categories.id')
|
||||
cats.map { |c| c.id }.sort
|
||||
end
|
||||
|
||||
def topic_create_allowed_category_ids
|
||||
Category.topic_create_allowed(self.id).select(:id)
|
||||
end
|
||||
|
||||
# Flag all posts from a user as spam
|
||||
def flag_linked_posts_as_spam
|
||||
admin = Discourse.system_user
|
||||
|
@ -660,6 +664,7 @@ end
|
|||
# topic_reply_count :integer default(0), not null
|
||||
# blocked :boolean default(FALSE)
|
||||
# dynamic_favicon :boolean default(FALSE), not null
|
||||
# title :string(255)
|
||||
#
|
||||
# Indexes
|
||||
#
|
||||
|
|
|
@ -179,7 +179,7 @@ ORDER BY p.created_at desc
|
|||
|
||||
# move into Topic perhaps
|
||||
group_ids = nil
|
||||
if topic && topic.category && topic.category.secure
|
||||
if topic && topic.category && topic.category.read_restricted
|
||||
group_ids = topic.category.groups.pluck("groups.id")
|
||||
end
|
||||
|
||||
|
@ -232,11 +232,11 @@ ORDER BY p.created_at desc
|
|||
unless guardian.is_staff?
|
||||
allowed = guardian.secure_category_ids
|
||||
if allowed.present?
|
||||
builder.where("( c.secure IS NULL OR
|
||||
c.secure = 'f' OR
|
||||
(c.secure = 't' and c.id in (:cats)) )", cats: guardian.secure_category_ids )
|
||||
builder.where("( c.read_restricted IS NULL OR
|
||||
NOT c.read_restricted OR
|
||||
(c.read_restricted and c.id in (:cats)) )", cats: guardian.secure_category_ids )
|
||||
else
|
||||
builder.where("(c.secure IS NULL OR c.secure = 'f')")
|
||||
builder.where("(c.read_restricted IS NULL OR NOT c.read_restricted)")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -9,6 +9,6 @@ class BasicCategorySerializer < ApplicationSerializer
|
|||
:description,
|
||||
:topic_url,
|
||||
:hotness,
|
||||
:secure
|
||||
:read_restricted
|
||||
|
||||
end
|
||||
|
|
|
@ -1,13 +1,24 @@
|
|||
class CategorySerializer < BasicCategorySerializer
|
||||
|
||||
attributes :secure, :groups, :available_groups, :auto_close_days
|
||||
attributes :read_restricted, :groups, :available_groups, :auto_close_days, :group_permissions
|
||||
|
||||
def groups
|
||||
@groups ||= object.groups.order("name").all.map(&:name)
|
||||
def group_permissions
|
||||
@group_permissions ||= begin
|
||||
perms = object.category_groups.joins(:group).order("groups.name").map do |cg|
|
||||
{
|
||||
permission_type: cg.permission_type,
|
||||
group_name: cg.group.name
|
||||
}
|
||||
end
|
||||
if perms.length == 0 && !object.read_restricted
|
||||
perms << {permission_type: CategoryGroup.permission_types[:full], group_name: :everyone}
|
||||
end
|
||||
perms
|
||||
end
|
||||
end
|
||||
|
||||
def available_groups
|
||||
Group.order("name").map(&:name) - groups
|
||||
Group.order("name").pluck(:name) - group_permissions.map{|g| g[:group_name]}
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -999,6 +999,11 @@ en:
|
|||
|
||||
browser_update: 'Unfortunately, <a href="http://www.discourse.org/faq/#browser">your browser is too old to work on this Discourse forum</a>. Please <a href="http://browsehappy.com">upgrade your browser</a>.'
|
||||
|
||||
permission_types:
|
||||
full: "Create Topics, Create Posts and Read"
|
||||
create_post: "Create Posts and Read"
|
||||
readonly: "Read Only"
|
||||
|
||||
# This section is exported to the javascript for i18n in the admin section
|
||||
admin_js:
|
||||
type_to_filter: "type to filter..."
|
||||
|
@ -1245,3 +1250,4 @@ en:
|
|||
title: 'Settings'
|
||||
reset: 'reset to default'
|
||||
none: 'none'
|
||||
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
class AddPermissionTypeToCategoryGroups < ActiveRecord::Migration
|
||||
def change
|
||||
# 1 is full permissions
|
||||
add_column :category_groups, :permission_type, :integer, default: 1
|
||||
|
||||
# secure is ambiguous after this change, it should be read_restricted
|
||||
rename_column :categories, :secure, :read_restricted
|
||||
end
|
||||
end
|
|
@ -7,6 +7,7 @@ class Guardian
|
|||
def staff?; false; end
|
||||
def approved?; false; end
|
||||
def secure_category_ids; []; end
|
||||
def topic_create_allowed_category_ids; []; end
|
||||
def has_trust_level?(level); false; end
|
||||
end
|
||||
|
||||
|
@ -328,7 +329,7 @@ class Guardian
|
|||
topic.deleted_at.nil? &&
|
||||
|
||||
# not secure, or I can see it
|
||||
(not(topic.secure_category?) || can_see_category?(topic.category)) &&
|
||||
(not(topic.read_restricted_category?) || can_see_category?(topic.category)) &&
|
||||
|
||||
# not private, or I am allowed (or an admin)
|
||||
(not(topic.private_message?) || authenticated? && (topic.all_allowed_users.where(id: @user.id).exists? || is_admin?))
|
||||
|
@ -340,7 +341,7 @@ class Guardian
|
|||
end
|
||||
|
||||
def can_see_category?(category)
|
||||
not(category.secure) || secure_category_ids.include?(category.id)
|
||||
not(category.read_restricted) || secure_category_ids.include?(category.id)
|
||||
end
|
||||
|
||||
def can_vote?(post, opts={})
|
||||
|
@ -378,6 +379,10 @@ class Guardian
|
|||
@secure_category_ids ||= @user.secure_category_ids
|
||||
end
|
||||
|
||||
def topic_create_allowed_category_ids
|
||||
@topic_create_allowed_category_ids ||= @user.topic_create_allowed_category_ids
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def is_my_own?(obj)
|
||||
|
|
|
@ -116,7 +116,7 @@ class PostCreator
|
|||
protected
|
||||
|
||||
def secure_group_ids(topic)
|
||||
@secure_group_ids ||= if topic.category && topic.category.secure?
|
||||
@secure_group_ids ||= if topic.category && topic.category.read_restricted?
|
||||
topic.category.secure_group_ids
|
||||
end
|
||||
end
|
||||
|
|
|
@ -160,9 +160,9 @@ class Search
|
|||
.order("topics.bumped_at DESC")
|
||||
|
||||
if secure_category_ids.present?
|
||||
posts = posts.where("(categories.id IS NULL) OR (NOT categories.secure) OR (categories.id IN (?))", secure_category_ids)
|
||||
posts = posts.where("(categories.id IS NULL) OR (NOT categories.read_restricted) OR (categories.id IN (?))", secure_category_ids)
|
||||
else
|
||||
posts = posts.where("(categories.id IS NULL) OR (NOT categories.secure)")
|
||||
posts = posts.where("(categories.id IS NULL) OR (NOT categories.read_restricted)")
|
||||
end
|
||||
posts.limit(limit)
|
||||
end
|
||||
|
|
|
@ -18,9 +18,9 @@ class SqlBuilder
|
|||
|
||||
def secure_category(secure_category_ids, category_alias = 'c')
|
||||
if secure_category_ids.present?
|
||||
where("NOT COALESCE(" << category_alias << ".secure, false) OR " << category_alias << ".id IN (:secure_category_ids)", secure_category_ids: secure_category_ids)
|
||||
where("NOT COALESCE(" << category_alias << ".read_restricted, false) OR " << category_alias << ".id IN (:secure_category_ids)", secure_category_ids: secure_category_ids)
|
||||
else
|
||||
where("NOT COALESCE(" << category_alias << ".secure, false)")
|
||||
where("NOT COALESCE(" << category_alias << ".read_restricted, false)")
|
||||
end
|
||||
self
|
||||
end
|
||||
|
|
|
@ -239,9 +239,9 @@ class TopicQuery
|
|||
unless @user && @user.moderator?
|
||||
category_ids = @user.secure_category_ids if @user
|
||||
if category_ids.present?
|
||||
result = result.where('categories.secure IS NULL OR categories.secure = ? OR categories.id IN (?)', false, category_ids)
|
||||
result = result.where('categories.read_restricted IS NULL OR categories.read_restricted = ? OR categories.id IN (?)', false, category_ids)
|
||||
else
|
||||
result = result.where('categories.secure IS NULL OR categories.secure = ?', false)
|
||||
result = result.where('categories.read_restricted IS NULL OR categories.read_restricted = ?', false)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -41,8 +41,7 @@ describe CategoryList do
|
|||
|
||||
cat = Fabricate(:category)
|
||||
topic = Fabricate(:topic, category: cat)
|
||||
cat.deny(:all)
|
||||
cat.allow(Group[:admins])
|
||||
cat.set_permissions(:admins => :full)
|
||||
cat.save
|
||||
|
||||
CategoryList.new(Guardian.new admin).categories.count.should == 1
|
||||
|
|
|
@ -215,8 +215,9 @@ describe Guardian do
|
|||
|
||||
it 'correctly handles groups' do
|
||||
group = Fabricate(:group)
|
||||
category = Fabricate(:category, secure: true)
|
||||
category.allow(group)
|
||||
category = Fabricate(:category, read_restricted: true)
|
||||
category.set_permissions(group => :full)
|
||||
category.save
|
||||
|
||||
topic = Fabricate(:topic, category: category)
|
||||
|
||||
|
|
|
@ -62,8 +62,7 @@ describe PostCreator do
|
|||
admin = Fabricate(:admin)
|
||||
|
||||
cat = Fabricate(:category)
|
||||
cat.deny(:all)
|
||||
cat.allow(Group[:admins])
|
||||
cat.set_permissions(:admins => :full)
|
||||
cat.save
|
||||
|
||||
created_post = nil
|
||||
|
|
|
@ -171,8 +171,7 @@ describe Search do
|
|||
topic.category_id = category.id
|
||||
topic.save
|
||||
|
||||
category.deny(:all)
|
||||
category.allow(Group[:staff])
|
||||
category.set_permissions(:staff => :full)
|
||||
category.save
|
||||
|
||||
result(nil).should_not be_present
|
||||
|
@ -211,7 +210,7 @@ describe Search do
|
|||
r[:title].should == category.name
|
||||
r[:url].should == "/category/#{category.slug}"
|
||||
|
||||
category.deny(:all)
|
||||
category.set_permissions({})
|
||||
category.save
|
||||
|
||||
result.should_not be_present
|
||||
|
|
|
@ -14,9 +14,8 @@ describe TopicQuery do
|
|||
context 'secure category' do
|
||||
it "filters categories out correctly" do
|
||||
category = Fabricate(:category)
|
||||
category.deny(:all)
|
||||
group = Fabricate(:group)
|
||||
category.allow(group)
|
||||
category.set_permissions(group => :full)
|
||||
category.save
|
||||
|
||||
topic = Fabricate(:topic, category: category)
|
||||
|
|
|
@ -134,11 +134,11 @@ describe CategoriesController do
|
|||
|
||||
describe 'success' do
|
||||
before do
|
||||
# might as well test this as well
|
||||
@category.allow(Group[:admins])
|
||||
# might as well test this while at it
|
||||
@category.set_permissions(:admins => :full)
|
||||
@category.save
|
||||
|
||||
xhr :put, :update, id: @category.id, name: 'science', color: '000', text_color: '0ff', group_names: Group[:staff].name, secure: 'true'
|
||||
xhr :put, :update, id: @category.id, name: 'science', color: '000', text_color: '0ff', group_names: Group[:staff].name, read_restricted: 'true'
|
||||
@category.reload
|
||||
end
|
||||
|
||||
|
@ -146,7 +146,7 @@ describe CategoriesController do
|
|||
@category.name.should == 'science'
|
||||
@category.color.should == '000'
|
||||
@category.text_color.should == '0ff'
|
||||
@category.secure?.should be_true
|
||||
@category.read_restricted?.should be_true
|
||||
@category.groups.count.should == 1
|
||||
end
|
||||
end
|
||||
|
|
|
@ -15,8 +15,7 @@ describe CategoryFeaturedTopic do
|
|||
# so much dancing, I am thinking fixures make sense here.
|
||||
user.change_trust_level!(:basic)
|
||||
|
||||
category.deny(:all)
|
||||
category.allow(Group[:trust_level_1])
|
||||
category.set_permissions(:trust_level_1 => :full)
|
||||
category.save
|
||||
|
||||
uncategorized_post = PostCreator.create(user, raw: "this is my new post 123 post", title: "hello world")
|
||||
|
|
|
@ -18,6 +18,57 @@ describe Category do
|
|||
it { should have_many :category_featured_topics }
|
||||
it { should have_many :featured_topics }
|
||||
|
||||
|
||||
describe "resolve_permissions" do
|
||||
it "can determine read_restricted" do
|
||||
read_restricted, resolved = Category.resolve_permissions(:everyone => :full)
|
||||
|
||||
read_restricted.should be_false
|
||||
resolved.should == []
|
||||
end
|
||||
end
|
||||
|
||||
describe "topic_create_allowed and post_create_allowed" do
|
||||
it "works" do
|
||||
default_category = Fabricate(:category)
|
||||
full_category = Fabricate(:category)
|
||||
can_post_category = Fabricate(:category)
|
||||
can_read_category = Fabricate(:category)
|
||||
|
||||
|
||||
user = Fabricate(:user)
|
||||
group = Fabricate(:group)
|
||||
group.add(user)
|
||||
group.save
|
||||
|
||||
admin = Fabricate(:admin)
|
||||
|
||||
full_category.set_permissions(group => :full)
|
||||
full_category.save
|
||||
|
||||
can_post_category.set_permissions(group => :create_post)
|
||||
can_post_category.save
|
||||
|
||||
can_read_category.set_permissions(group => :readonly)
|
||||
can_read_category.save
|
||||
|
||||
guardian = Guardian.new(admin)
|
||||
Category.topic_create_allowed(guardian).count.should == 4
|
||||
Category.post_create_allowed(guardian).count.should == 4
|
||||
Category.secured(guardian).count.should == 4
|
||||
|
||||
guardian = Guardian.new(user)
|
||||
Category.secured(guardian).count.should == 4
|
||||
Category.post_create_allowed(guardian).count.should == 3
|
||||
Category.topic_create_allowed(guardian).count.should == 2 # explicitly allowed once, default allowed once
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
describe "post_create_allowed" do
|
||||
|
||||
end
|
||||
|
||||
describe "security" do
|
||||
let(:category) { Fabricate(:category) }
|
||||
let(:category_2) { Fabricate(:category) }
|
||||
|
@ -25,20 +76,20 @@ describe Category do
|
|||
let(:group) { Fabricate(:group) }
|
||||
|
||||
it "secures categories correctly" do
|
||||
category.secure?.should be_false
|
||||
category.read_restricted?.should be_false
|
||||
|
||||
category.deny(:all)
|
||||
category.secure?.should be_true
|
||||
category.set_permissions({})
|
||||
category.read_restricted?.should be_true
|
||||
|
||||
category.allow(:all)
|
||||
category.secure?.should be_false
|
||||
category.set_permissions(:everyone => :full)
|
||||
category.read_restricted?.should be_false
|
||||
|
||||
user.secure_categories.should be_empty
|
||||
|
||||
group.add(user)
|
||||
group.save
|
||||
|
||||
category.allow(group)
|
||||
category.set_permissions(group.id => :full)
|
||||
category.save
|
||||
|
||||
user.reload
|
||||
|
@ -47,13 +98,13 @@ describe Category do
|
|||
|
||||
it "lists all secured categories correctly" do
|
||||
group.add(user)
|
||||
category.allow(group)
|
||||
category.set_permissions(group.id => :full)
|
||||
category.save
|
||||
category_2.set_permissions(group.id => :full)
|
||||
category_2.save
|
||||
|
||||
Category.secured.should == [category]
|
||||
|
||||
category_2.allow(group)
|
||||
|
||||
Category.secured.should =~ [category, category_2]
|
||||
Category.secured.should =~ []
|
||||
Category.secured(Guardian.new(user)).should =~ [category, category_2]
|
||||
end
|
||||
end
|
||||
|
||||
|
|
16
spec/models/site_spec.rb
Normal file
16
spec/models/site_spec.rb
Normal file
|
@ -0,0 +1,16 @@
|
|||
require 'spec_helper'
|
||||
require_dependency 'site'
|
||||
|
||||
describe Site do
|
||||
it "omits categories users can not write to from the category list" do
|
||||
category = Fabricate(:category)
|
||||
user = Fabricate(:user)
|
||||
|
||||
Site.new(Guardian.new(user)).categories.count.should == 1
|
||||
|
||||
category.set_permissions(:everyone => :create_post)
|
||||
category.save
|
||||
|
||||
Site.new(Guardian.new(user)).categories.count.should == 0
|
||||
end
|
||||
end
|
|
@ -258,8 +258,7 @@ describe TopicLink do
|
|||
TopicLink.topic_summary(Guardian.new, post.topic_id).count.should == 1
|
||||
TopicLink.counts_for(Guardian.new, post.topic, [post]).length.should == 1
|
||||
|
||||
category.deny(:all)
|
||||
category.allow(Group[:staff])
|
||||
category.set_permissions(:staff => :full)
|
||||
category.save
|
||||
|
||||
admin = Fabricate(:admin)
|
||||
|
|
|
@ -192,7 +192,7 @@ describe Topic do
|
|||
context "secure categories" do
|
||||
|
||||
let(:user) { Fabricate(:user) }
|
||||
let(:category) { Fabricate(:category, secure: true) }
|
||||
let(:category) { Fabricate(:category, read_restricted: true) }
|
||||
|
||||
before do
|
||||
topic.category = category
|
||||
|
@ -1263,7 +1263,7 @@ describe Topic do
|
|||
|
||||
describe 'secured' do
|
||||
it 'can remove secure groups' do
|
||||
category = Fabricate(:category, secure: true)
|
||||
category = Fabricate(:category, read_restricted: true)
|
||||
topic = Fabricate(:topic, category: category)
|
||||
|
||||
Topic.secured(Guardian.new(nil)).count.should == 0
|
||||
|
@ -1280,17 +1280,17 @@ describe Topic do
|
|||
let(:category){ Category.new }
|
||||
|
||||
it "is true if the category is secure" do
|
||||
category.stubs(:secure).returns(true)
|
||||
Topic.new(:category => category).should be_secure_category
|
||||
category.stubs(:read_restricted).returns(true)
|
||||
Topic.new(:category => category).should be_read_restricted_category
|
||||
end
|
||||
|
||||
it "is false if the category is not secure" do
|
||||
category.stubs(:secure).returns(false)
|
||||
Topic.new(:category => category).should_not be_secure_category
|
||||
category.stubs(:read_restricted).returns(false)
|
||||
Topic.new(:category => category).should_not be_read_restricted_category
|
||||
end
|
||||
|
||||
it "is false if there is no category" do
|
||||
Topic.new(:category => nil).should_not be_secure_category
|
||||
Topic.new(:category => nil).should_not be_read_restricted_category
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -51,7 +51,7 @@ describe TopicTrackingState do
|
|||
row.user_id.should == post.user_id
|
||||
|
||||
# when we have no permission to see a category, don't show its stats
|
||||
category = Fabricate(:category, secure: true)
|
||||
category = Fabricate(:category, read_restricted: true)
|
||||
|
||||
post.topic.category_id = category.id
|
||||
post.topic.save
|
||||
|
|
|
@ -68,7 +68,7 @@ describe UserAction do
|
|||
|
||||
# groups
|
||||
|
||||
category = Fabricate(:category, secure: true)
|
||||
category = Fabricate(:category, read_restricted: true)
|
||||
|
||||
public_topic.recover!
|
||||
public_topic.category = category
|
||||
|
@ -82,7 +82,7 @@ describe UserAction do
|
|||
group.add(u)
|
||||
group.save
|
||||
|
||||
category.allow(group)
|
||||
category.set_permissions(group => :full)
|
||||
category.save
|
||||
|
||||
stats_for_user(u).should == [UserAction::NEW_TOPIC]
|
||||
|
|
Loading…
Reference in a new issue