[deploy] Create private group chat channel for orgs (#7270)
* WIP chat channel for orgs * Finalize * Enable upsert functionality for role switch * Private group chat * Rearrange permissions code * fix typo
This commit is contained in:
parent
2343785927
commit
03c28a5671
7 changed files with 109 additions and 30 deletions
|
|
@ -35,6 +35,10 @@ class ChatChannel < ApplicationRecord
|
|||
channel_type != "direct"
|
||||
end
|
||||
|
||||
def private_org_channel?
|
||||
channel_name.to_s.ends_with?(" private group chat") #e.g. @devteam private group chat
|
||||
end
|
||||
|
||||
def clear_channel
|
||||
messages.destroy_all
|
||||
Pusher.trigger(pusher_channels, "channel-cleared", { chat_channel_id: id }.to_json)
|
||||
|
|
|
|||
|
|
@ -8,10 +8,37 @@ class OrganizationMembership < ApplicationRecord
|
|||
validates :user_id, uniqueness: { scope: :organization_id }
|
||||
validates :type_of_user, inclusion: { in: USER_TYPES }
|
||||
|
||||
after_create :update_user_organization_info_updated_at
|
||||
after_save :upsert_chat_channel_membership
|
||||
after_create :update_user_organization_info_updated_at
|
||||
after_destroy :update_user_organization_info_updated_at
|
||||
|
||||
def update_user_organization_info_updated_at
|
||||
user.touch(:organization_info_updated_at)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def upsert_chat_channel_membership
|
||||
return if type_of_user == "guest"
|
||||
|
||||
role = type_of_user == "admin" ? "mod" : "member"
|
||||
name = "@#{organization.slug} private group chat"
|
||||
channel = ChatChannel.find_by(channel_name: name)
|
||||
if channel
|
||||
add_chat_channel_membership(user, channel, role)
|
||||
else
|
||||
ChatChannel.create_with_users(
|
||||
users: [user],
|
||||
channel_type: "invite_only",
|
||||
contrived_name: name,
|
||||
membership_role: role,
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
def add_chat_channel_membership(user, channel, role)
|
||||
membership = ChatChannelMembership.find_or_initialize_by(user_id: user.id, chat_channel_id: channel.id)
|
||||
membership.role = role
|
||||
membership.save
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -39,7 +39,8 @@ class ChatChannelPolicy < ApplicationPolicy
|
|||
|
||||
def user_can_edit_channel
|
||||
record.present? &&
|
||||
(user.has_role?(:super_admin) || record.channel_mod_ids.include?(user.id))
|
||||
(user.has_role?(:super_admin) || record.channel_mod_ids.include?(user.id)) &&
|
||||
!record.private_org_channel?
|
||||
end
|
||||
|
||||
def user_part_of_channel_or_open
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@
|
|||
<div class="settings-form-userlist">
|
||||
<h3>Members</h3>
|
||||
<% @channel.active_memberships.includes(:user).each do |active_membership| %>
|
||||
<div class="settings-form-userlistrow" >
|
||||
<div class="settings-form-userlistrow">
|
||||
<a href="<%= active_membership.user.path %>"><img src="<%= ProfileImage.new(active_membership.user).get(width: 90) %>" /><%= active_membership.user.name %></a>
|
||||
<% if @membership.role == "mod" && active_membership.role != "mod" %>
|
||||
<%= form_tag "/chat_channel_memberships/remove_membership", class: "inline-button-form-hidden" do %>
|
||||
|
|
@ -36,7 +36,7 @@
|
|||
<div class="settings-form-userlist">
|
||||
<h3>Pending Invitations</h3>
|
||||
<% @channel.pending_memberships.includes(:user).each do |pending_membership| %>
|
||||
<div class="settings-form-userlistrow" >
|
||||
<div class="settings-form-userlistrow">
|
||||
<a href="<%= pending_membership.user.path %>"><img src="<%= ProfileImage.new(pending_membership.user).get(width: 90) %>" /><%= pending_membership.user.name %></a>
|
||||
<%= form_tag "/chat_channel_memberships/remove_membership", class: "inline-button-form" do %>
|
||||
<%= hidden_field_tag(:status, "pending") %>
|
||||
|
|
@ -47,32 +47,33 @@
|
|||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
<%= form_for ChatChannelMembership.new do |f| %>
|
||||
<div class="field">
|
||||
<%= f.hidden_field :chat_channel_id, value: @channel.id %>
|
||||
<%= f.label "Usernames to Invite" %>
|
||||
<%= f.text_field :invitation_usernames, placeholder: "Comma separated" %>
|
||||
</div>
|
||||
<div class="field">
|
||||
<%= f.submit "SUBMIT", class: "cta" %>
|
||||
</div>
|
||||
<% if @channel.private_org_channel? %>
|
||||
<p><em>Invite new members by <a href="/settings/organization">inviting them to this organization.</a></em></p>
|
||||
<% else %>
|
||||
<%= form_for ChatChannelMembership.new do |f| %>
|
||||
<div class="field">
|
||||
<%= f.hidden_field :chat_channel_id, value: @channel.id %>
|
||||
<%= f.label "Usernames to Invite" %>
|
||||
<%= f.text_field :invitation_usernames, placeholder: "Comma separated" %>
|
||||
</div>
|
||||
<div class="field">
|
||||
<%= f.submit "SUBMIT", class: "cta" %>
|
||||
</div>
|
||||
<% end %>
|
||||
<hr class="settings-hr" />
|
||||
<h1>Channel Settings</h1>
|
||||
<%= form_for(@channel) do |f| %>
|
||||
<div class="field">
|
||||
<%= f.label :description %>
|
||||
<%= f.text_area :description %>
|
||||
</div>
|
||||
<div class="field">
|
||||
<%= f.submit "SUBMIT", class: "cta" %>
|
||||
</div>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<% if @membership.role == "mod" %>
|
||||
<hr class="settings-hr"/>
|
||||
<h1>Channel Settings</h1>
|
||||
<h3>Notifications</h3>
|
||||
<%= form_for(@channel) do |f| %>
|
||||
<div class="field">
|
||||
<%= f.label :description %>
|
||||
<%= f.text_area :description %>
|
||||
</div>
|
||||
<div class="field">
|
||||
<%= f.submit "SUBMIT", class: "cta" %>
|
||||
</div>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<hr class="settings-hr"/>
|
||||
<hr class="settings-hr" />
|
||||
<h1>Personal Settings</h1>
|
||||
<h3>Notifications</h3>
|
||||
<%= form_for(@membership) do |f| %>
|
||||
|
|
@ -84,12 +85,12 @@
|
|||
<%= f.submit "SUBMIT", class: "cta" %>
|
||||
</div>
|
||||
<% end %>
|
||||
<hr class="settings-hr"/>
|
||||
<hr class="settings-hr" />
|
||||
<% if @membership.role == "mod" %>
|
||||
<p>Questions about Connect Channel moderation? Contact <a href="mailto:<%= SiteConfig.default_site_email %>"><%= SiteConfig.default_site_email %></a></p>
|
||||
<% else %>
|
||||
<h3>Danger Zone</h3>
|
||||
<%= form_for(@membership, html: { method: :delete, onsubmit: "return confirm('Are you absolutely sure you want to leave this channel? This action is permanent.');" } ) do |f| %>
|
||||
<%= form_for(@membership, html: { method: :delete, onsubmit: "return confirm('Are you absolutely sure you want to leave this channel? This action is permanent.');" }) do |f| %>
|
||||
<div class="field">
|
||||
<%= f.submit "LEAVE CHANNEL", class: "cta cta-danger" %>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -56,4 +56,16 @@ RSpec.describe ChatChannel, type: :model do
|
|||
expect(chat_channel.chat_channel_memberships.exists?(user_id: users.first.id)).to be(false)
|
||||
end
|
||||
end
|
||||
|
||||
describe "#private_org_channel?" do
|
||||
it "detects private org channel if name matches" do
|
||||
chat_channel.channel_name = "@org private group chat"
|
||||
expect(chat_channel.private_org_channel?).to be(true)
|
||||
end
|
||||
|
||||
it "detects not private org channel if name does not match" do
|
||||
chat_channel.channel_name = "@org magoo"
|
||||
expect(chat_channel.private_org_channel?).to be(false)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -4,10 +4,31 @@ RSpec.describe OrganizationMembership, type: :model do
|
|||
describe "validations" do
|
||||
subject { build(:organization_membership) }
|
||||
|
||||
let(:organization) { create(:organization) }
|
||||
|
||||
it { is_expected.to validate_presence_of(:user_id) }
|
||||
it { is_expected.to validate_presence_of(:organization_id) }
|
||||
it { is_expected.to validate_presence_of(:type_of_user) }
|
||||
it { is_expected.to validate_uniqueness_of(:user_id).scoped_to(:organization_id) }
|
||||
it { is_expected.to validate_inclusion_of(:type_of_user).in_array(OrganizationMembership::USER_TYPES) }
|
||||
|
||||
it "creates member chat channel after save" do
|
||||
create(:organization_membership, type_of_user: "member", organization: organization)
|
||||
expect(ChatChannelMembership.last.role).to eq("member")
|
||||
expect(ChatChannelMembership.last.chat_channel.channel_name).to eq("@#{organization.slug} private group chat")
|
||||
end
|
||||
|
||||
it "adds user to existing org chat channel after save" do
|
||||
chat_channel = create(:chat_channel, channel_name: "@#{organization.slug} private group chat")
|
||||
organization_membership = create(:organization_membership, type_of_user: "member", organization: organization)
|
||||
expect(chat_channel.active_users).to include(organization_membership.user)
|
||||
end
|
||||
|
||||
it "updates chat channel membership if org membership is updated" do
|
||||
organization_membership = create(:organization_membership, type_of_user: "member", organization: organization)
|
||||
expect(ChatChannelMembership.last.role).to eq("member")
|
||||
organization_membership.update(type_of_user: "admin")
|
||||
expect(ChatChannelMembership.last.role).to eq("mod")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -157,6 +157,19 @@ RSpec.describe "ChatChannelMemberships", type: :request do
|
|||
expect(ChatChannelMembership.all.size).to eq(chat_channel_members_count + 1)
|
||||
expect(ChatChannelMembership.last.status).to eq("pending")
|
||||
end
|
||||
|
||||
it "disallows invitation creation when org private group" do
|
||||
chat_channel.update_column(:channel_name, "@org private group chat")
|
||||
chat_channel.chat_channel_memberships.where(user_id: user.id).update(role: "mod")
|
||||
expect do
|
||||
post "/chat_channel_memberships", params: {
|
||||
chat_channel_membership: {
|
||||
invitation_usernames: second_user.username.to_s,
|
||||
chat_channel_id: chat_channel.id
|
||||
}
|
||||
}
|
||||
end.to raise_error(Pundit::NotAuthorizedError)
|
||||
end
|
||||
end
|
||||
|
||||
context "when user is not authorized to add channel membership" do
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue