mirror of
https://github.com/discourse/discourse.git
synced 2025-09-06 10:50:21 +08:00
backend for secure categories mostly done (todo pm groups)
This commit is contained in:
parent
a99efecb39
commit
5cfcdc7ef0
16 changed files with 353 additions and 238 deletions
|
@ -13,6 +13,9 @@ class Category < ActiveRecord::Base
|
||||||
has_many :category_featured_users
|
has_many :category_featured_users
|
||||||
has_many :featured_users, through: :category_featured_users, source: :user
|
has_many :featured_users, through: :category_featured_users, source: :user
|
||||||
|
|
||||||
|
has_many :category_groups
|
||||||
|
has_many :groups, through: :category_groups
|
||||||
|
|
||||||
validates :user_id, presence: true
|
validates :user_id, presence: true
|
||||||
validates :name, presence: true, uniqueness: true, length: { in: 1..50 }
|
validates :name, presence: true, uniqueness: true, length: { in: 1..50 }
|
||||||
validate :uncategorized_validator
|
validate :uncategorized_validator
|
||||||
|
@ -28,6 +31,32 @@ class Category < ActiveRecord::Base
|
||||||
|
|
||||||
delegate :post_template, to: 'self.class'
|
delegate :post_template, to: 'self.class'
|
||||||
|
|
||||||
|
# Internal: Update category stats: # of topics in past year, month, week for
|
||||||
|
# all categories.
|
||||||
|
def self.update_stats
|
||||||
|
topics = Topic
|
||||||
|
.select("COUNT(*)")
|
||||||
|
.where("topics.category_id = categories.id")
|
||||||
|
.where("categories.topic_id <> topics.id")
|
||||||
|
.visible
|
||||||
|
|
||||||
|
topic_count = topics.to_sql
|
||||||
|
topics_year = topics.created_since(1.year.ago).to_sql
|
||||||
|
topics_month = topics.created_since(1.month.ago).to_sql
|
||||||
|
topics_week = topics.created_since(1.week.ago).to_sql
|
||||||
|
|
||||||
|
Category.update_all("topic_count = (#{topic_count}),
|
||||||
|
topics_year = (#{topics_year}),
|
||||||
|
topics_month = (#{topics_month}),
|
||||||
|
topics_week = (#{topics_week})")
|
||||||
|
end
|
||||||
|
|
||||||
|
# Internal: Generate the text of post prompting to enter category
|
||||||
|
# description.
|
||||||
|
def self.post_template
|
||||||
|
I18n.t("category.post_template", replace_paragraph: I18n.t("category.replace_paragraph"))
|
||||||
|
end
|
||||||
|
|
||||||
def create_category_definition
|
def create_category_definition
|
||||||
create_topic!(title: I18n.t("category.topic_prefix", category: name), user: user, pinned_at: Time.now)
|
create_topic!(title: I18n.t("category.topic_prefix", category: name), user: user, pinned_at: Time.now)
|
||||||
update_column(:topic_id, topic.id)
|
update_column(:topic_id, topic.id)
|
||||||
|
@ -66,29 +95,23 @@ class Category < ActiveRecord::Base
|
||||||
errors.add(:slug, I18n.t(:is_reserved)) if slug == SiteSetting.uncategorized_name
|
errors.add(:slug, I18n.t(:is_reserved)) if slug == SiteSetting.uncategorized_name
|
||||||
end
|
end
|
||||||
|
|
||||||
# Internal: Update category stats: # of topics in past year, month, week for
|
def secure?
|
||||||
# all categories.
|
self.secure
|
||||||
def self.update_stats
|
|
||||||
topics = Topic
|
|
||||||
.select("COUNT(*)")
|
|
||||||
.where("topics.category_id = categories.id")
|
|
||||||
.where("categories.topic_id <> topics.id")
|
|
||||||
.visible
|
|
||||||
|
|
||||||
topic_count = topics.to_sql
|
|
||||||
topics_year = topics.created_since(1.year.ago).to_sql
|
|
||||||
topics_month = topics.created_since(1.month.ago).to_sql
|
|
||||||
topics_week = topics.created_since(1.week.ago).to_sql
|
|
||||||
|
|
||||||
Category.update_all("topic_count = (#{topic_count}),
|
|
||||||
topics_year = (#{topics_year}),
|
|
||||||
topics_month = (#{topics_month}),
|
|
||||||
topics_week = (#{topics_week})")
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Internal: Generate the text of post prompting to enter category
|
def deny(group)
|
||||||
# description.
|
if group == :all
|
||||||
def self.post_template
|
self.secure = true
|
||||||
I18n.t("category.post_template", replace_paragraph: I18n.t("category.replace_paragraph"))
|
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def allow(group)
|
||||||
|
if group == :all
|
||||||
|
self.secure = false
|
||||||
|
category_groups.clear
|
||||||
|
else
|
||||||
|
groups.push(group)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
4
app/models/category_group.rb
Normal file
4
app/models/category_group.rb
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
class CategoryGroup < ActiveRecord::Base
|
||||||
|
belongs_to :category
|
||||||
|
belongs_to :group
|
||||||
|
end
|
|
@ -1,5 +1,15 @@
|
||||||
class Group < ActiveRecord::Base
|
class Group < ActiveRecord::Base
|
||||||
|
has_many :category_groups
|
||||||
|
has_many :group_users
|
||||||
|
|
||||||
|
has_many :categories, through: :category_groups
|
||||||
|
has_many :users, through: :group_users
|
||||||
|
|
||||||
def self.builtin
|
def self.builtin
|
||||||
Enum.new(:moderators, :admins, :trust_level_1, :trust_level_2)
|
Enum.new(:moderators, :admins, :trust_level_1, :trust_level_2)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def add(user)
|
||||||
|
self.users.push(user)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,2 +1,4 @@
|
||||||
class GroupUser < ActiveRecord::Base
|
class GroupUser < ActiveRecord::Base
|
||||||
|
belongs_to :group
|
||||||
|
belongs_to :user
|
||||||
end
|
end
|
||||||
|
|
|
@ -68,7 +68,7 @@ class Topic < ActiveRecord::Base
|
||||||
|
|
||||||
scope :listable_topics, lambda { where('topics.archetype <> ?', [Archetype.private_message]) }
|
scope :listable_topics, lambda { where('topics.archetype <> ?', [Archetype.private_message]) }
|
||||||
|
|
||||||
scope :by_newest, order('created_at desc, id desc')
|
scope :by_newest, order('topics.created_at desc, topics.id desc')
|
||||||
|
|
||||||
# Helps us limit how many favorites can be made in a day
|
# Helps us limit how many favorites can be made in a day
|
||||||
class FavoriteLimiter < RateLimiter
|
class FavoriteLimiter < RateLimiter
|
||||||
|
|
|
@ -26,6 +26,10 @@ class User < ActiveRecord::Base
|
||||||
has_one :github_user_info, dependent: :destroy
|
has_one :github_user_info, dependent: :destroy
|
||||||
belongs_to :approved_by, class_name: 'User'
|
belongs_to :approved_by, class_name: 'User'
|
||||||
|
|
||||||
|
has_many :group_users
|
||||||
|
has_many :groups, through: :group_users
|
||||||
|
has_many :secure_categories, through: :groups, source: :categories
|
||||||
|
|
||||||
validates_presence_of :username
|
validates_presence_of :username
|
||||||
validates_presence_of :email
|
validates_presence_of :email
|
||||||
validates_uniqueness_of :email
|
validates_uniqueness_of :email
|
||||||
|
@ -150,10 +154,6 @@ class User < ActiveRecord::Base
|
||||||
u.errors[:username].blank?
|
u.errors[:username].blank?
|
||||||
end
|
end
|
||||||
|
|
||||||
def enqueue_welcome_message(message_type)
|
|
||||||
return unless SiteSetting.send_welcome_message?
|
|
||||||
Jobs.enqueue(:send_system_message, user_id: id, message_type: message_type)
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.suggest_name(email)
|
def self.suggest_name(email)
|
||||||
return "" unless email
|
return "" unless email
|
||||||
|
@ -162,6 +162,25 @@ class User < ActiveRecord::Base
|
||||||
name.titleize
|
name.titleize
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Find a user by temporary key, nil if not found or key is invalid
|
||||||
|
def self.find_by_temporary_key(key)
|
||||||
|
user_id = $redis.get("temporary_key:#{key}")
|
||||||
|
if user_id.present?
|
||||||
|
where(id: user_id.to_i).first
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.find_by_username_or_email(username_or_email)
|
||||||
|
lower_user = username_or_email.downcase
|
||||||
|
lower_email = Email.downcase(username_or_email)
|
||||||
|
where("username_lower = :user or lower(username) = :user or email = :email or lower(name) = :user", user: lower_user, email: lower_email)
|
||||||
|
end
|
||||||
|
|
||||||
|
def enqueue_welcome_message(message_type)
|
||||||
|
return unless SiteSetting.send_welcome_message?
|
||||||
|
Jobs.enqueue(:send_system_message, user_id: id, message_type: message_type)
|
||||||
|
end
|
||||||
|
|
||||||
def change_username(new_username)
|
def change_username(new_username)
|
||||||
current_username, self.username = username, new_username
|
current_username, self.username = username, new_username
|
||||||
|
|
||||||
|
@ -185,19 +204,6 @@ class User < ActiveRecord::Base
|
||||||
key
|
key
|
||||||
end
|
end
|
||||||
|
|
||||||
# Find a user by temporary key, nil if not found or key is invalid
|
|
||||||
def self.find_by_temporary_key(key)
|
|
||||||
user_id = $redis.get("temporary_key:#{key}")
|
|
||||||
if user_id.present?
|
|
||||||
where(id: user_id.to_i).first
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.find_by_username_or_email(username_or_email)
|
|
||||||
lower_user = username_or_email.downcase
|
|
||||||
lower_email = Email.downcase(username_or_email)
|
|
||||||
where("username_lower = :user or lower(username) = :user or email = :email or lower(name) = :user", user: lower_user, email: lower_email)
|
|
||||||
end
|
|
||||||
|
|
||||||
# tricky, we need our bus to be subscribed from the right spot
|
# tricky, we need our bus to be subscribed from the right spot
|
||||||
def sync_notification_channel_position
|
def sync_notification_channel_position
|
||||||
|
@ -510,6 +516,11 @@ class User < ActiveRecord::Base
|
||||||
.count
|
.count
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def secure_category_ids
|
||||||
|
cats = self.moderator? ? Category.select(:id).where(:secure, true) : secure_categories.select('categories.id')
|
||||||
|
cats.map{|c| c.id}
|
||||||
|
end
|
||||||
|
|
||||||
protected
|
protected
|
||||||
|
|
||||||
def cook
|
def cook
|
||||||
|
@ -591,4 +602,5 @@ class User < ActiveRecord::Base
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -35,27 +35,28 @@ class UserAction < ActiveRecord::Base
|
||||||
EDIT
|
EDIT
|
||||||
].each_with_index.to_a.flatten]
|
].each_with_index.to_a.flatten]
|
||||||
|
|
||||||
|
|
||||||
def self.stats(user_id, guardian)
|
def self.stats(user_id, guardian)
|
||||||
results = UserAction.select("action_type, COUNT(*) count")
|
|
||||||
.joins(:target_topic)
|
|
||||||
.where(user_id: user_id)
|
|
||||||
.group('action_type')
|
|
||||||
|
|
||||||
# We apply similar filters in stream, might consider trying to consolidate somehow
|
# Sam: I tried this in AR and it got complex
|
||||||
unless guardian.can_see_private_messages?(user_id)
|
builder = UserAction.sql_builder <<SQL
|
||||||
results = results.where('topics.archetype <> ?', Archetype::private_message)
|
|
||||||
end
|
|
||||||
|
|
||||||
unless guardian.user && guardian.user.id == user_id
|
SELECT action_type, COUNT(*) count
|
||||||
results = results.where("action_type <> ?", BOOKMARK)
|
FROM user_actions a
|
||||||
end
|
JOIN topics t ON t.id = a.target_topic_id
|
||||||
|
LEFT JOIN posts p on p.id = a.target_post_id
|
||||||
|
JOIN posts p2 on p2.topic_id = a.target_topic_id and p2.post_number = 1
|
||||||
|
LEFT JOIN categories c ON c.id = t.category_id
|
||||||
|
/*where*/
|
||||||
|
GROUP BY action_type
|
||||||
|
SQL
|
||||||
|
|
||||||
unless guardian.can_see_deleted_posts?
|
|
||||||
results = results.where('topics.deleted_at IS NULL')
|
|
||||||
end
|
|
||||||
|
|
||||||
results = results.to_a
|
builder.where('a.user_id = :user_id', user_id: user_id)
|
||||||
|
|
||||||
|
apply_common_filters(builder, user_id, guardian)
|
||||||
|
|
||||||
|
results = builder.exec.to_a
|
||||||
results.sort! { |a,b| ORDER[a.action_type] <=> ORDER[b.action_type] }
|
results.sort! { |a,b| ORDER[a.action_type] <=> ORDER[b.action_type] }
|
||||||
|
|
||||||
results
|
results
|
||||||
|
@ -93,23 +94,14 @@ JOIN posts p2 on p2.topic_id = a.target_topic_id and p2.post_number = 1
|
||||||
JOIN users u on u.id = a.acting_user_id
|
JOIN users u on u.id = a.acting_user_id
|
||||||
JOIN users pu on pu.id = COALESCE(p.user_id, t.user_id)
|
JOIN users pu on pu.id = COALESCE(p.user_id, t.user_id)
|
||||||
JOIN users au on au.id = a.user_id
|
JOIN users au on au.id = a.user_id
|
||||||
|
LEFT JOIN categories c on c.id = t.category_id
|
||||||
/*where*/
|
/*where*/
|
||||||
/*order_by*/
|
/*order_by*/
|
||||||
/*offset*/
|
/*offset*/
|
||||||
/*limit*/
|
/*limit*/
|
||||||
")
|
")
|
||||||
|
|
||||||
unless guardian.can_see_deleted_posts?
|
apply_common_filters(builder, user_id, guardian, ignore_private_messages)
|
||||||
builder.where("p.deleted_at is null and p2.deleted_at is null and t.deleted_at is null")
|
|
||||||
end
|
|
||||||
|
|
||||||
unless guardian.user && guardian.user.id == user_id
|
|
||||||
builder.where("a.action_type not in (#{BOOKMARK})")
|
|
||||||
end
|
|
||||||
|
|
||||||
if !guardian.can_see_private_messages?(user_id) || ignore_private_messages
|
|
||||||
builder.where("t.archetype != :archetype", archetype: Archetype::private_message)
|
|
||||||
end
|
|
||||||
|
|
||||||
if action_id
|
if action_id
|
||||||
builder.where("a.id = :id", id: action_id.to_i)
|
builder.where("a.id = :id", id: action_id.to_i)
|
||||||
|
@ -182,6 +174,34 @@ JOIN users au on au.id = a.user_id
|
||||||
end
|
end
|
||||||
|
|
||||||
protected
|
protected
|
||||||
|
|
||||||
|
def self.apply_common_filters(builder,user_id,guardian,ignore_private_messages=false)
|
||||||
|
|
||||||
|
|
||||||
|
unless guardian.can_see_deleted_posts?
|
||||||
|
builder.where("p.deleted_at is null and p2.deleted_at is null and t.deleted_at is null")
|
||||||
|
end
|
||||||
|
|
||||||
|
unless guardian.user && guardian.user.id == user_id
|
||||||
|
builder.where("a.action_type not in (#{BOOKMARK})")
|
||||||
|
end
|
||||||
|
|
||||||
|
if !guardian.can_see_private_messages?(user_id) || ignore_private_messages
|
||||||
|
builder.where("t.archetype != :archetype", archetype: Archetype::private_message)
|
||||||
|
end
|
||||||
|
|
||||||
|
unless guardian.is_moderator?
|
||||||
|
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 )
|
||||||
|
else
|
||||||
|
builder.where("(c.secure IS NULL OR c.secure = 'f')")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def self.require_parameters(data, *params)
|
def self.require_parameters(data, *params)
|
||||||
params.each do |p|
|
params.each do |p|
|
||||||
raise Discourse::InvalidParameters.new(p) if data[p].nil?
|
raise Discourse::InvalidParameters.new(p) if data[p].nil?
|
||||||
|
|
11
db/migrate/20130429000101_add_security_to_categories.rb
Normal file
11
db/migrate/20130429000101_add_security_to_categories.rb
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
class AddSecurityToCategories < ActiveRecord::Migration
|
||||||
|
def change
|
||||||
|
add_column :categories, :secure, :boolean, default: false, null: false
|
||||||
|
|
||||||
|
create_table :category_groups do |t|
|
||||||
|
t.integer :category_id, null: false
|
||||||
|
t.integer :group_id, null: false
|
||||||
|
t.timestamps
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -12,7 +12,11 @@ class Guardian
|
||||||
end
|
end
|
||||||
|
|
||||||
def is_admin?
|
def is_admin?
|
||||||
!@user.nil? && @user.admin?
|
@user && @user.admin?
|
||||||
|
end
|
||||||
|
|
||||||
|
def is_moderator?
|
||||||
|
@user && @user.moderator?
|
||||||
end
|
end
|
||||||
|
|
||||||
# Can the user see the object?
|
# Can the user see the object?
|
||||||
|
@ -329,6 +333,15 @@ class Guardian
|
||||||
end
|
end
|
||||||
|
|
||||||
def can_see_topic?(topic)
|
def can_see_topic?(topic)
|
||||||
|
return false unless topic
|
||||||
|
|
||||||
|
return true if @user && @user.moderator?
|
||||||
|
return false if topic.deleted_at.present?
|
||||||
|
|
||||||
|
if topic.category && topic.category.secure
|
||||||
|
return false unless @user && can_see_category?(topic.category)
|
||||||
|
end
|
||||||
|
|
||||||
if topic.private_message?
|
if topic.private_message?
|
||||||
return false if @user.blank?
|
return false if @user.blank?
|
||||||
return true if topic.allowed_users.include?(@user)
|
return true if topic.allowed_users.include?(@user)
|
||||||
|
@ -337,6 +350,22 @@ class Guardian
|
||||||
true
|
true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def can_see_post?(post)
|
||||||
|
return false unless post
|
||||||
|
|
||||||
|
return true if @user && @user.moderator?
|
||||||
|
return false if post.deleted_at.present?
|
||||||
|
|
||||||
|
can_see_topic?(post.topic)
|
||||||
|
end
|
||||||
|
|
||||||
|
def can_see_category?(category)
|
||||||
|
return true unless category.secure
|
||||||
|
return false unless @user
|
||||||
|
|
||||||
|
@user.secure_category_ids.include?(category.id)
|
||||||
|
end
|
||||||
|
|
||||||
def can_vote?(post, opts={})
|
def can_vote?(post, opts={})
|
||||||
post_can_act?(post,:vote, opts)
|
post_can_act?(post,:vote, opts)
|
||||||
end
|
end
|
||||||
|
@ -371,4 +400,7 @@ class Guardian
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def secure_category_ids
|
||||||
|
@user ? @user.secure_category_ids : []
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -207,13 +207,13 @@ class TopicQuery
|
||||||
# Start with a list of all topics
|
# Start with a list of all topics
|
||||||
result = Topic
|
result = Topic
|
||||||
|
|
||||||
if @user_id.present?
|
if @user_id
|
||||||
result = result.joins("LEFT OUTER JOIN topic_users AS tu ON (topics.id = tu.topic_id AND tu.user_id = #{@user_id})")
|
result = result.joins("LEFT OUTER JOIN topic_users AS tu ON (topics.id = tu.topic_id AND tu.user_id = #{@user_id})")
|
||||||
end
|
end
|
||||||
|
|
||||||
unless query_opts[:unordered]
|
unless query_opts[:unordered]
|
||||||
# If we're logged in, we have to pay attention to our pinned settings
|
# If we're logged in, we have to pay attention to our pinned settings
|
||||||
if @user_id.present?
|
if @user
|
||||||
result = result.order(TopicQuery.order_nocategory_with_pinned_sql)
|
result = result.order(TopicQuery.order_nocategory_with_pinned_sql)
|
||||||
else
|
else
|
||||||
result = result.order(TopicQuery.order_nocategory_basic_bumped)
|
result = result.order(TopicQuery.order_nocategory_basic_bumped)
|
||||||
|
@ -227,6 +227,16 @@ class TopicQuery
|
||||||
result = result.visible if @user.blank? or @user.regular?
|
result = result.visible if @user.blank? or @user.regular?
|
||||||
result = result.where('topics.id <> ?', query_opts[:except_topic_id]) if query_opts[:except_topic_id].present?
|
result = result.where('topics.id <> ?', query_opts[:except_topic_id]) if query_opts[:except_topic_id].present?
|
||||||
result = result.offset(query_opts[:page].to_i * page_size) if query_opts[:page].present?
|
result = result.offset(query_opts[:page].to_i * page_size) if query_opts[:page].present?
|
||||||
|
|
||||||
|
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)
|
||||||
|
else
|
||||||
|
result = result.where('categories.secure IS NULL OR categories.secure = ?', false)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
result
|
result
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -144,28 +144,14 @@ describe Guardian do
|
||||||
|
|
||||||
let(:topic) { Fabricate(:topic, user: coding_horror)}
|
let(:topic) { Fabricate(:topic, user: coding_horror)}
|
||||||
|
|
||||||
it 'returns false when the post is nil' do
|
it 'displays visibility correctly' do
|
||||||
Guardian.new(user).can_see_post_actors?(nil, PostActionType.types[:like]).should be_false
|
guardian = Guardian.new(user)
|
||||||
end
|
guardian.can_see_post_actors?(nil, PostActionType.types[:like]).should be_false
|
||||||
|
guardian.can_see_post_actors?(topic, PostActionType.types[:like]).should be_true
|
||||||
it 'returns true for likes' do
|
guardian.can_see_post_actors?(topic, PostActionType.types[:bookmark]).should be_false
|
||||||
Guardian.new(user).can_see_post_actors?(topic, PostActionType.types[:like]).should be_true
|
guardian.can_see_post_actors?(topic, PostActionType.types[:off_topic]).should be_false
|
||||||
end
|
guardian.can_see_post_actors?(topic, PostActionType.types[:spam]).should be_false
|
||||||
|
guardian.can_see_post_actors?(topic, PostActionType.types[:vote]).should be_true
|
||||||
it 'returns false for bookmarks' do
|
|
||||||
Guardian.new(user).can_see_post_actors?(topic, PostActionType.types[:bookmark]).should be_false
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'returns false for off-topic flags' do
|
|
||||||
Guardian.new(user).can_see_post_actors?(topic, PostActionType.types[:off_topic]).should be_false
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'returns false for spam flags' do
|
|
||||||
Guardian.new(user).can_see_post_actors?(topic, PostActionType.types[:spam]).should be_false
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'returns true for public votes' do
|
|
||||||
Guardian.new(user).can_see_post_actors?(topic, PostActionType.types[:vote]).should be_true
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns false for private votes' do
|
it 'returns false for private votes' do
|
||||||
|
@ -176,34 +162,15 @@ describe Guardian do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'can_impersonate?' do
|
describe 'can_impersonate?' do
|
||||||
it 'returns false when the target is nil' do
|
it 'allows impersonation correctly' do
|
||||||
Guardian.new(admin).can_impersonate?(nil).should be_false
|
Guardian.new(admin).can_impersonate?(nil).should be_false
|
||||||
end
|
|
||||||
|
|
||||||
it 'returns false when the user is nil' do
|
|
||||||
Guardian.new.can_impersonate?(user).should be_false
|
Guardian.new.can_impersonate?(user).should be_false
|
||||||
end
|
|
||||||
|
|
||||||
it "doesn't allow a non-admin to impersonate someone" do
|
|
||||||
Guardian.new(coding_horror).can_impersonate?(user).should be_false
|
Guardian.new(coding_horror).can_impersonate?(user).should be_false
|
||||||
end
|
|
||||||
|
|
||||||
it "doesn't allow an admin to impersonate themselves" do
|
|
||||||
Guardian.new(admin).can_impersonate?(admin).should be_false
|
Guardian.new(admin).can_impersonate?(admin).should be_false
|
||||||
end
|
|
||||||
|
|
||||||
it "doesn't allow an admin to impersonate another admin" do
|
|
||||||
Guardian.new(admin).can_impersonate?(another_admin).should be_false
|
Guardian.new(admin).can_impersonate?(another_admin).should be_false
|
||||||
end
|
|
||||||
|
|
||||||
it "allows an admin to impersonate a regular user" do
|
|
||||||
Guardian.new(admin).can_impersonate?(user).should be_true
|
Guardian.new(admin).can_impersonate?(user).should be_true
|
||||||
end
|
|
||||||
|
|
||||||
it "allows an admin to impersonate a moderator" do
|
|
||||||
Guardian.new(admin).can_impersonate?(moderator).should be_true
|
Guardian.new(admin).can_impersonate?(moderator).should be_true
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'can_invite_to?' do
|
describe 'can_invite_to?' do
|
||||||
|
@ -211,16 +178,11 @@ describe Guardian do
|
||||||
let(:user) { topic.user }
|
let(:user) { topic.user }
|
||||||
let(:moderator) { Fabricate(:moderator) }
|
let(:moderator) { Fabricate(:moderator) }
|
||||||
|
|
||||||
it 'returns false with a nil user' do
|
it 'handles invitation correctly' do
|
||||||
Guardian.new(nil).can_invite_to?(topic).should be_false
|
Guardian.new(nil).can_invite_to?(topic).should be_false
|
||||||
end
|
|
||||||
|
|
||||||
it 'returns false with a nil object' do
|
|
||||||
Guardian.new(moderator).can_invite_to?(nil).should be_false
|
Guardian.new(moderator).can_invite_to?(nil).should be_false
|
||||||
end
|
|
||||||
|
|
||||||
it 'returns true for a moderator to invite' do
|
|
||||||
Guardian.new(moderator).can_invite_to?(topic).should be_true
|
Guardian.new(moderator).can_invite_to?(topic).should be_true
|
||||||
|
Guardian.new(user).can_invite_to?(topic).should be_false
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns false when the site requires approving users' do
|
it 'returns false when the site requires approving users' do
|
||||||
|
@ -228,10 +190,6 @@ describe Guardian do
|
||||||
Guardian.new(moderator).can_invite_to?(topic).should be_false
|
Guardian.new(moderator).can_invite_to?(topic).should be_false
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns false for a regular user to invite' do
|
|
||||||
Guardian.new(user).can_invite_to?(topic).should be_false
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'can_see?' do
|
describe 'can_see?' do
|
||||||
|
@ -244,6 +202,40 @@ describe Guardian do
|
||||||
it 'allows non logged in users to view topics' do
|
it 'allows non logged in users to view topics' do
|
||||||
Guardian.new.can_see?(topic).should be_true
|
Guardian.new.can_see?(topic).should be_true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'correctly handles groups' do
|
||||||
|
group = Fabricate(:group)
|
||||||
|
category = Fabricate(:category, secure: true)
|
||||||
|
category.allow(group)
|
||||||
|
|
||||||
|
topic = Fabricate(:topic, category: category)
|
||||||
|
|
||||||
|
Guardian.new(user).can_see?(topic).should be_false
|
||||||
|
group.add(user)
|
||||||
|
group.save
|
||||||
|
|
||||||
|
Guardian.new(user).can_see?(topic).should be_true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'a Post' do
|
||||||
|
it 'correctly handles post visibility' do
|
||||||
|
Guardian.new(user).can_see?(post).should be_true
|
||||||
|
|
||||||
|
post.destroy
|
||||||
|
post.reload
|
||||||
|
Guardian.new(user).can_see?(post).should be_false
|
||||||
|
Guardian.new(admin).can_see?(post).should be_true
|
||||||
|
|
||||||
|
post.recover
|
||||||
|
post.reload
|
||||||
|
topic.destroy
|
||||||
|
topic.reload
|
||||||
|
Guardian.new(user).can_see?(post).should be_false
|
||||||
|
Guardian.new(admin).can_see?(post).should be_true
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -3,13 +3,38 @@ require 'topic_view'
|
||||||
|
|
||||||
describe TopicQuery do
|
describe TopicQuery do
|
||||||
|
|
||||||
let!(:user) { Fabricate(:coding_horror) }
|
let(:user) { Fabricate(:coding_horror) }
|
||||||
let(:creator) { Fabricate(:user) }
|
let(:creator) { Fabricate(:user) }
|
||||||
let(:topic_query) { TopicQuery.new(user) }
|
let(:topic_query) { TopicQuery.new(user) }
|
||||||
|
|
||||||
let(:moderator) { Fabricate(:moderator) }
|
let(:moderator) { Fabricate(:moderator) }
|
||||||
let(:admin) { Fabricate(:moderator) }
|
let(:admin) { Fabricate(:moderator) }
|
||||||
|
|
||||||
|
|
||||||
|
context 'secure category' do
|
||||||
|
it "filters categories out correctly" do
|
||||||
|
category = Fabricate(:category)
|
||||||
|
category.deny(:all)
|
||||||
|
group = Fabricate(:group)
|
||||||
|
category.allow(group)
|
||||||
|
category.save
|
||||||
|
|
||||||
|
topic = Fabricate(:topic, category: category)
|
||||||
|
|
||||||
|
TopicQuery.new(nil).list_latest.topics.count.should == 0
|
||||||
|
TopicQuery.new(user).list_latest.topics.count.should == 0
|
||||||
|
|
||||||
|
# mods can see every group
|
||||||
|
TopicQuery.new(moderator).list_latest.topics.count.should == 2
|
||||||
|
|
||||||
|
group.add(user)
|
||||||
|
group.save
|
||||||
|
|
||||||
|
TopicQuery.new(user).list_latest.topics.count.should == 2
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
context 'a bunch of topics' do
|
context 'a bunch of topics' do
|
||||||
let!(:regular_topic) { Fabricate(:topic, title: 'this is a regular topic', user: creator, bumped_at: 15.minutes.ago) }
|
let!(:regular_topic) { Fabricate(:topic, title: 'this is a regular topic', user: creator, bumped_at: 15.minutes.ago) }
|
||||||
let!(:pinned_topic) { Fabricate(:topic, title: 'this is a pinned topic', user: creator, pinned_at: 10.minutes.ago, bumped_at: 10.minutes.ago) }
|
let!(:pinned_topic) { Fabricate(:topic, title: 'this is a pinned topic', user: creator, pinned_at: 10.minutes.ago, bumped_at: 10.minutes.ago) }
|
||||||
|
@ -90,17 +115,11 @@ describe TopicQuery do
|
||||||
|
|
||||||
context 'with no data' do
|
context 'with no data' do
|
||||||
|
|
||||||
it "has no read topics" do
|
|
||||||
topic_query.list_unread.topics.should be_blank
|
|
||||||
end
|
|
||||||
|
|
||||||
it "has no unread topics" do
|
it "has no unread topics" do
|
||||||
topic_query.list_unread.topics.should be_blank
|
topic_query.list_unread.topics.should be_blank
|
||||||
end
|
|
||||||
|
|
||||||
it "has an unread count of 0" do
|
|
||||||
topic_query.unread_count.should == 0
|
topic_query.unread_count.should == 0
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'with read data' do
|
context 'with read data' do
|
||||||
|
@ -115,9 +134,6 @@ describe TopicQuery do
|
||||||
context 'list_unread' do
|
context 'list_unread' do
|
||||||
it 'contains no topics' do
|
it 'contains no topics' do
|
||||||
topic_query.list_unread.topics.should == []
|
topic_query.list_unread.topics.should == []
|
||||||
end
|
|
||||||
|
|
||||||
it "returns 0 as the unread count" do
|
|
||||||
topic_query.unread_count.should == 0
|
topic_query.unread_count.should == 0
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
3
spec/fabricators/group_fabricator.rb
Normal file
3
spec/fabricators/group_fabricator.rb
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
Fabricator(:group) do
|
||||||
|
name 'my group'
|
||||||
|
end
|
|
@ -18,6 +18,34 @@ describe Category do
|
||||||
it { should have_many :category_featured_topics }
|
it { should have_many :category_featured_topics }
|
||||||
it { should have_many :featured_topics }
|
it { should have_many :featured_topics }
|
||||||
|
|
||||||
|
describe "security" do
|
||||||
|
it "secures categories correctly" do
|
||||||
|
category = Fabricate(:category)
|
||||||
|
|
||||||
|
category.secure?.should be_false
|
||||||
|
|
||||||
|
category.deny(:all)
|
||||||
|
category.secure?.should be_true
|
||||||
|
|
||||||
|
category.allow(:all)
|
||||||
|
category.secure?.should be_false
|
||||||
|
|
||||||
|
user = Fabricate(:user)
|
||||||
|
user.secure_categories.to_a.should == []
|
||||||
|
|
||||||
|
group = Fabricate(:group)
|
||||||
|
group.add(user)
|
||||||
|
group.save
|
||||||
|
|
||||||
|
category.allow(group)
|
||||||
|
category.save
|
||||||
|
|
||||||
|
user.reload
|
||||||
|
user.secure_categories.to_a.should == [category]
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe "uncategorized name" do
|
describe "uncategorized name" do
|
||||||
let(:category) { Fabricate.build(:category, name: SiteSetting.uncategorized_name) }
|
let(:category) { Fabricate.build(:category, name: SiteSetting.uncategorized_name) }
|
||||||
|
|
||||||
|
@ -71,47 +99,27 @@ describe Category do
|
||||||
@topic = @category.topic
|
@topic = @category.topic
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'creates a slug' do
|
it 'is created correctly' do
|
||||||
@category.slug.should == 'amazing-category'
|
@category.slug.should == 'amazing-category'
|
||||||
end
|
|
||||||
|
|
||||||
it "has a hotness of 5.0 by default" do
|
|
||||||
@category.hotness.should == 5.0
|
@category.hotness.should == 5.0
|
||||||
end
|
|
||||||
|
|
||||||
it 'has a default description' do
|
|
||||||
@category.description.should be_blank
|
@category.description.should be_blank
|
||||||
end
|
|
||||||
|
|
||||||
it 'has one topic' do
|
|
||||||
Topic.where(category_id: @category).count.should == 1
|
Topic.where(category_id: @category).count.should == 1
|
||||||
end
|
|
||||||
|
|
||||||
it 'creates a topic post' do
|
|
||||||
@topic.should be_present
|
@topic.should be_present
|
||||||
end
|
|
||||||
|
|
||||||
it 'points back to itself' do
|
|
||||||
@topic.category.should == @category
|
@topic.category.should == @category
|
||||||
end
|
|
||||||
|
|
||||||
it 'is a visible topic' do
|
|
||||||
@topic.should be_visible
|
@topic.should be_visible
|
||||||
end
|
|
||||||
|
|
||||||
it 'is pinned' do
|
|
||||||
@topic.pinned_at.should be_present
|
@topic.pinned_at.should be_present
|
||||||
end
|
|
||||||
|
|
||||||
it 'is an undeletable topic' do
|
|
||||||
Guardian.new(@category.user).can_delete?(@topic).should be_false
|
Guardian.new(@category.user).can_delete?(@topic).should be_false
|
||||||
end
|
|
||||||
|
|
||||||
it 'should have one post' do
|
|
||||||
@topic.posts.count.should == 1
|
@topic.posts.count.should == 1
|
||||||
end
|
|
||||||
|
|
||||||
it 'should have a topic url' do
|
|
||||||
@category.topic_url.should be_present
|
@category.topic_url.should be_present
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -129,17 +137,12 @@ describe Category do
|
||||||
@category.reload
|
@category.reload
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'still has 0 forum topics' do
|
it 'does not cause changes' do
|
||||||
@category.topic_count.should == 0
|
@category.topic_count.should == 0
|
||||||
end
|
|
||||||
|
|
||||||
it "didn't change the category" do
|
|
||||||
@topic.category.should == @category
|
@topic.category.should == @category
|
||||||
end
|
|
||||||
|
|
||||||
it "didn't change the category's forum topic" do
|
|
||||||
@category.topic.should == @topic
|
@category.topic.should == @topic
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -151,11 +154,8 @@ describe Category do
|
||||||
@category.destroy
|
@category.destroy
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'deletes the category' do
|
it 'is deleted correctly' do
|
||||||
Category.exists?(id: @category_id).should be_false
|
Category.exists?(id: @category_id).should be_false
|
||||||
end
|
|
||||||
|
|
||||||
it 'deletes the forum topic' do
|
|
||||||
Topic.exists?(id: @topic_id).should be_false
|
Topic.exists?(id: @topic_id).should be_false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -172,21 +172,13 @@ describe Category do
|
||||||
@category.reload
|
@category.reload
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'updates topics_week' do
|
it 'updates topic stats' do
|
||||||
@category.topics_week.should == 1
|
@category.topics_week.should == 1
|
||||||
end
|
|
||||||
|
|
||||||
it 'updates topics_month' do
|
|
||||||
@category.topics_month.should == 1
|
@category.topics_month.should == 1
|
||||||
end
|
|
||||||
|
|
||||||
it 'updates topics_year' do
|
|
||||||
@category.topics_year.should == 1
|
@category.topics_year.should == 1
|
||||||
end
|
|
||||||
|
|
||||||
it 'updates topic_count' do
|
|
||||||
@category.topic_count.should == 1
|
@category.topic_count.should == 1
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'with deleted topics' do
|
context 'with deleted topics' do
|
||||||
|
@ -197,21 +189,13 @@ describe Category do
|
||||||
@category.reload
|
@category.reload
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'does not count deleted topics for topics_week' do
|
it 'does not count deleted topics' do
|
||||||
@category.topics_week.should == 0
|
@category.topics_week.should == 0
|
||||||
end
|
@category.topic_count.should == 0
|
||||||
|
|
||||||
it 'does not count deleted topics for topics_month' do
|
|
||||||
@category.topics_month.should == 0
|
@category.topics_month.should == 0
|
||||||
end
|
|
||||||
|
|
||||||
it 'does not count deleted topics for topics_year' do
|
|
||||||
@category.topics_year.should == 0
|
@category.topics_year.should == 0
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'does not count deleted topics for topic_count' do
|
|
||||||
@category.topic_count.should == 0
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
4
spec/models/group_spec.rb
Normal file
4
spec/models/group_spec.rb
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
describe Group do
|
||||||
|
end
|
|
@ -37,41 +37,52 @@ describe UserAction do
|
||||||
log_test_action(action_type: UserAction::BOOKMARK)
|
log_test_action(action_type: UserAction::BOOKMARK)
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'stats' do
|
def stats_for_user(viewer=nil)
|
||||||
|
UserAction.stats(user.id, Guardian.new(viewer)).map{|r| r["action_type"].to_i}.sort
|
||||||
let :mystats do
|
|
||||||
UserAction.stats(user.id, Guardian.new(user))
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'include correct events' do
|
def stream_count(viewer=nil)
|
||||||
mystats.map{|r| r["action_type"].to_i}.should include(UserAction::NEW_TOPIC)
|
UserAction.stream(user_id: user.id, guardian: Guardian.new(viewer)).count
|
||||||
UserAction.stats(user.id,Guardian.new).map{|r| r["action_type"].to_i}.should_not include(UserAction::NEW_PRIVATE_MESSAGE)
|
|
||||||
UserAction.stats(user.id,Guardian.new).map{|r| r["action_type"].to_i}.should_not include(UserAction::GOT_PRIVATE_MESSAGE)
|
|
||||||
mystats.map{|r| r["action_type"].to_i}.should include(UserAction::NEW_PRIVATE_MESSAGE)
|
|
||||||
mystats.map{|r| r["action_type"].to_i}.should include(UserAction::GOT_PRIVATE_MESSAGE)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'should not include new topic when topic is deleted' do
|
it 'includes the events correctly' do
|
||||||
|
|
||||||
|
mystats = stats_for_user(user)
|
||||||
|
expecting = [UserAction::NEW_TOPIC, UserAction::NEW_PRIVATE_MESSAGE, UserAction::GOT_PRIVATE_MESSAGE, UserAction::BOOKMARK].sort
|
||||||
|
mystats.should == expecting
|
||||||
|
stream_count(user).should == 4
|
||||||
|
|
||||||
|
other_stats = stats_for_user
|
||||||
|
expecting = [UserAction::NEW_TOPIC]
|
||||||
|
stream_count.should == 1
|
||||||
|
|
||||||
|
other_stats.should == expecting
|
||||||
|
|
||||||
public_topic.destroy
|
public_topic.destroy
|
||||||
mystats.map{|r| r["action_type"].to_i}.should_not include(UserAction::NEW_TOPIC)
|
stats_for_user.should == []
|
||||||
end
|
stream_count.should == 0
|
||||||
|
|
||||||
end
|
# groups
|
||||||
|
|
||||||
describe 'stream' do
|
category = Fabricate(:category, secure: true)
|
||||||
|
|
||||||
it 'should have 1 item for non owners' do
|
public_topic.recover
|
||||||
UserAction.stream(user_id: user.id, guardian: Guardian.new).count.should == 1
|
public_topic.category = category
|
||||||
end
|
public_topic.save
|
||||||
|
|
||||||
it 'should include no items for non owner when topic deleted' do
|
stats_for_user.should == []
|
||||||
public_topic.destroy
|
stream_count.should == 0
|
||||||
UserAction.stream(user_id: user.id, guardian: Guardian.new).count.should == 0
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'should have bookmarks and pms for owners' do
|
group = Fabricate(:group)
|
||||||
UserAction.stream(user_id: user.id, guardian: user.guardian).count.should == 4
|
u = Fabricate(:coding_horror)
|
||||||
end
|
group.add(u)
|
||||||
|
group.save
|
||||||
|
|
||||||
|
category.allow(group)
|
||||||
|
category.save
|
||||||
|
|
||||||
|
stats_for_user(u).should == [UserAction::NEW_TOPIC]
|
||||||
|
stream_count(u).should == 1
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -146,8 +157,6 @@ describe UserAction do
|
||||||
end
|
end
|
||||||
it 'should exist' do
|
it 'should exist' do
|
||||||
@action.should_not be_nil
|
@action.should_not be_nil
|
||||||
end
|
|
||||||
it 'shoule have the correct date' do
|
|
||||||
@action.created_at.should be_within(1).of(@post.topic.created_at)
|
@action.created_at.should be_within(1).of(@post.topic.created_at)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -164,16 +173,11 @@ describe UserAction do
|
||||||
@response = Fabricate(:post, reply_to_post_number: 1, topic: @post.topic, user: @other_user, raw: "perhaps @#{@mentioned.username} knows how this works?")
|
@response = Fabricate(:post, reply_to_post_number: 1, topic: @post.topic, user: @other_user, raw: "perhaps @#{@mentioned.username} knows how this works?")
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'should log a post action for the poster' do
|
it 'should log user actions correctly' do
|
||||||
@response.user.user_actions.where(action_type: UserAction::POST).first.should_not be_nil
|
@response.user.user_actions.where(action_type: UserAction::POST).first.should_not be_nil
|
||||||
end
|
|
||||||
|
|
||||||
it 'should log a post action for the original poster' do
|
|
||||||
@post.user.user_actions.where(action_type: UserAction::RESPONSE).first.should_not be_nil
|
@post.user.user_actions.where(action_type: UserAction::RESPONSE).first.should_not be_nil
|
||||||
end
|
|
||||||
|
|
||||||
it 'should log a mention for the mentioned' do
|
|
||||||
@mentioned.user_actions.where(action_type: UserAction::MENTION).first.should_not be_nil
|
@mentioned.user_actions.where(action_type: UserAction::MENTION).first.should_not be_nil
|
||||||
|
@post.user.user_actions.joins(:target_post).where('posts.post_number = 2').count.should == 1
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'should not log a double notification for a post edit' do
|
it 'should not log a double notification for a post edit' do
|
||||||
|
@ -182,12 +186,7 @@ describe UserAction do
|
||||||
@response.user.user_actions.where(action_type: UserAction::POST).count.should == 1
|
@response.user.user_actions.where(action_type: UserAction::POST).count.should == 1
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'should not log topic reply and reply for a single post' do
|
|
||||||
@post.user.user_actions.joins(:target_post).where('posts.post_number = 2').count.should == 1
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'when user bookmarks' do
|
describe 'when user bookmarks' do
|
||||||
|
@ -198,19 +197,12 @@ describe UserAction do
|
||||||
@action = @user.user_actions.where(action_type: UserAction::BOOKMARK).first
|
@action = @user.user_actions.where(action_type: UserAction::BOOKMARK).first
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'should create a bookmark action' do
|
it 'should create a bookmark action correctly' do
|
||||||
@action.action_type.should == UserAction::BOOKMARK
|
@action.action_type.should == UserAction::BOOKMARK
|
||||||
end
|
|
||||||
it 'should point to the correct post' do
|
|
||||||
@action.target_post_id.should == @post.id
|
@action.target_post_id.should == @post.id
|
||||||
end
|
|
||||||
it 'should have the right acting_user' do
|
|
||||||
@action.acting_user_id.should == @user.id
|
@action.acting_user_id.should == @user.id
|
||||||
end
|
|
||||||
it 'should target the correct user' do
|
|
||||||
@action.user_id.should == @user.id
|
@action.user_id.should == @user.id
|
||||||
end
|
|
||||||
it 'should nuke the action when unbookmarked' do
|
|
||||||
PostAction.remove_act(@user, @post, PostActionType.types[:bookmark])
|
PostAction.remove_act(@user, @post, PostActionType.types[:bookmark])
|
||||||
@user.user_actions.where(action_type: UserAction::BOOKMARK).first.should be_nil
|
@user.user_actions.where(action_type: UserAction::BOOKMARK).first.should be_nil
|
||||||
end
|
end
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue