Skip to content

Commit

Permalink
Merge pull request #3247 from jmay/group-manager-invites
Browse files Browse the repository at this point in the history
group manager can issue invitations from restricted topics
  • Loading branch information
ZogStriP committed Mar 16, 2015
2 parents c5de75d + 0f36774 commit 3a40875
Show file tree
Hide file tree
Showing 8 changed files with 132 additions and 7 deletions.
1 change: 1 addition & 0 deletions app/assets/javascripts/discourse/controllers/invite.js.es6
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export default ObjectController.extend(ModalFunctionality, {
if (this.get('saving')) return true;
if (this.blank('email')) return true;
if (!Discourse.Utilities.emailValid(this.get('email'))) return true;
if (this.get('model.details.can_invite_to')) return false;
if (this.get('isPrivateTopic') && this.blank('groupNames')) return true;
return false;
}.property('email', 'isPrivateTopic', 'groupNames', 'saving'),
Expand Down
22 changes: 21 additions & 1 deletion app/models/invite.rb
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,21 @@ def redeem
end


def add_groups_for_topic(topic)
if topic.category
(topic.category.groups - groups).each { |group| group.add(user) }
end
end

def self.extend_permissions(topic, user, invited_by)
if topic.private_message?
topic.grant_permission_to_user(user.email)
elsif topic.category && topic.category.groups.any?
if Guardian.new(invited_by).can_invite_to?(topic)
(topic.category.groups - user.groups).each { |group| group.add(user) }
end
end
end
# Create an invite for a user, supplying an optional topic
#
# Return the previously existing invite if already exists. Returns nil if the invite can't be created.
Expand All @@ -64,7 +79,7 @@ def self.invite_by_email(email, invited_by, topic=nil, group_ids=nil)
user = User.find_by(email: lower_email)

if user
topic.grant_permission_to_user(lower_email) if topic && topic.private_message?
extend_permissions(topic, user, invited_by) if topic
return nil
end

Expand Down Expand Up @@ -93,6 +108,11 @@ def self.invite_by_email(email, invited_by, topic=nil, group_ids=nil)
group_ids.each do |group_id|
invite.invited_groups.create!(group_id: group_id)
end
else
if topic && topic.category # && Guardian.new(invited_by).can_invite_to?(topic)
group_ids = topic.category.groups.pluck(:id) - invite.invited_groups.pluck(:group_id)
group_ids.each { |group_id| invite.invited_groups.create!(group_id: group_id) }
end
end

Jobs.enqueue(:invite_email, invite_id: invite.id)
Expand Down
25 changes: 21 additions & 4 deletions lib/guardian.rb
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,12 @@ def can_see_invite_details?(user)
is_me?(user)
end

def invitations_allowed?
!SiteSetting.enable_sso &&
SiteSetting.enable_local_logins &&
(!SiteSetting.must_approve_users? || is_staff?)
end

def can_invite_to_forum?(groups=nil)
authenticated? &&
!SiteSetting.enable_sso &&
Expand All @@ -209,10 +215,21 @@ def can_invite_to_forum?(groups=nil)
end

def can_invite_to?(object, group_ids=nil)
can_invite = can_see?(object) && can_invite_to_forum? && ( group_ids.blank? || is_admin? )
#TODO how should invite to PM work?
can_invite = can_invite && ( !object.category.read_restricted || is_admin? ) if object.is_a?(Topic) && object.category
can_invite
return false if ! authenticated?
return false if ! invitations_allowed?
return true if is_admin?
return false if ! can_see?(object)

return false if group_ids.present?

if object.is_a?(Topic) && object.category
if object.category.groups.any?
return true if object.category.groups.all? { |g| can_edit_group?(g) }
end
return false if object.category.read_restricted
end

user.has_trust_level?(TrustLevel[2])
end

def can_bulk_invite_to_forum?(user)
Expand Down
6 changes: 6 additions & 0 deletions spec/components/guardian_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,9 @@
let(:user) { topic.user }
let(:moderator) { Fabricate(:moderator) }
let(:admin) { Fabricate(:admin) }
let(:private_category) { Fabricate(:private_category, group: group) }
let(:group_private_topic) { Fabricate(:topic, category: private_category) }
let(:group_manager) { group_private_topic.user.tap { |u| group.add(u); group.appoint_manager(u) } }

it 'handles invitation correctly' do
expect(Guardian.new(nil).can_invite_to?(topic)).to be_falsey
Expand Down Expand Up @@ -298,6 +301,9 @@
expect(Guardian.new(admin).can_invite_to?(private_topic)).to be_truthy
end

it 'returns true for a group manager' do
expect(Guardian.new(group_manager).can_invite_to?(group_private_topic)).to be_truthy
end
end

describe 'can_see?' do
Expand Down
16 changes: 15 additions & 1 deletion spec/controllers/topics_controller_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -790,6 +790,20 @@
expect { xhr :post, :invite, topic_id: 1, email: 'jake@adventuretime.ooo' }.to raise_error(Discourse::NotLoggedIn)
end

describe 'when logged in as group manager' do
let(:group_manager) { log_in }
let(:group) { Fabricate(:group).tap { |g| g.add(group_manager); g.appoint_manager(group_manager) } }
let(:private_category) { Fabricate(:private_category, group: group) }
let(:group_private_topic) { Fabricate(:topic, category: private_category, user: group_manager) }
let(:recipient) { 'jake@adventuretime.ooo' }

it "should attach group to the invite" do
xhr :post, :invite, topic_id: group_private_topic.id, user: recipient
expect(response).to be_success
expect(Invite.find_by(email: recipient).groups).to eq([group])
end
end

describe 'when logged in' do
before do
@topic = Fabricate(:topic, user: log_in)
Expand All @@ -806,7 +820,7 @@
end
end

describe 'with permission' do
describe 'with admin permission' do

let!(:admin) do
log_in :admin
Expand Down
12 changes: 12 additions & 0 deletions spec/fabricators/category_fabricator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,15 @@
slug 'happy'
user
end

Fabricator(:private_category, from: :category) do
transient :group

name 'Private Category'
slug 'private'
user
after_build do |cat, transients|
cat.update!(read_restricted: true)
cat.category_groups.build(group_id: transients[:group].id, permission_type: :full)
end
end
22 changes: 22 additions & 0 deletions spec/models/invite_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,28 @@
end
end

context 'to a group-private topic' do
let(:group) { Fabricate(:group) }
let(:private_category) { Fabricate(:private_category, group: group) }
let(:group_private_topic) { Fabricate(:topic, category: private_category) }
let(:inviter) { group_private_topic.user }

before do
@invite = group_private_topic.invite_by_email(inviter, iceking)
end

it 'should add the groups to the invite' do
expect(@invite.groups).to eq([group])
end

context 'when duplicated' do
it 'should not duplicate the groups' do
expect(group_private_topic.invite_by_email(inviter, iceking)).to eq(@invite)
expect(@invite.groups).to eq([group])
end
end
end

context 'an existing user' do
let(:topic) { Fabricate(:topic, category_id: nil, archetype: 'private_message') }
let(:coding_horror) { Fabricate(:coding_horror) }
Expand Down
35 changes: 34 additions & 1 deletion spec/models/topic_spec.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# encoding: UTF-8
# encoding: utf-8

require 'spec_helper'
require_dependency 'post_destroyer'
Expand Down Expand Up @@ -1352,4 +1352,37 @@ def build_topic_with_title(title)
topic.last_posted_at = 1.minute.ago
expect(topic.save).to eq(true)
end

context 'invite by group manager' do
let(:group_manager) { Fabricate(:user) }
let(:group) { Fabricate(:group).tap { |g| g.add(group_manager); g.appoint_manager(group_manager) } }
let(:private_category) { Fabricate(:private_category, group: group) }
let(:group_private_topic) { Fabricate(:topic, category: private_category, user: group_manager) }

context 'to an email' do
let(:randolph) { 'randolph@duke.ooo' }

it "should attach group to the invite" do
invite = group_private_topic.invite(group_manager, randolph)
expect(invite.groups).to eq([group])
end
end

# should work for an existing user - give access, send notification
context 'to an existing user' do
let(:walter) { Fabricate(:walter_white) }

it "should add user to the group" do
expect(Guardian.new(walter).can_see?(group_private_topic)).to be_falsey
invite = group_private_topic.invite(group_manager, walter.email)
expect(invite).to be_nil
expect(walter.groups).to include(group)
expect(Guardian.new(walter).can_see?(group_private_topic)).to be_truthy
end
end

context 'to a previously-invited user' do

end
end
end

0 comments on commit 3a40875

Please sign in to comment.