Add organization membership functionality to internal (#3541)
This commit is contained in:
parent
5d13752e34
commit
96449fcc8f
15 changed files with 135 additions and 22 deletions
6
app/assets/stylesheets/internal.scss
Normal file
6
app/assets/stylesheets/internal.scss
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
header.internal-edit-header {
|
||||
display: flex;
|
||||
h2 {
|
||||
flex-grow: 1;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
class Internal::OrganizationMembershipsController < Internal::ApplicationController
|
||||
layout "internal"
|
||||
|
||||
def update
|
||||
organization_membership = OrganizationMembership.find_by(id: params[:id])
|
||||
if organization_membership.update(organization_membership_params)
|
||||
flash[:success] = "User was successfully updated to #{organization_membership.type_of_user}"
|
||||
else
|
||||
flash[:danger] = organization_membership.errors.full_messages
|
||||
end
|
||||
redirect_to internal_user_path(organization_membership.user_id)
|
||||
end
|
||||
|
||||
def create
|
||||
organization_membership = OrganizationMembership.new(organization_membership_params)
|
||||
organization = Organization.find_by(id: organization_membership_params[:organization_id])
|
||||
if organization && organization_membership.save
|
||||
flash[:success] = "User was successfully added to #{organization.name}"
|
||||
elsif organization.blank?
|
||||
flash[:danger] = "Organization ##{organization_membership_params[:organization_id]} does not exist. Perhaps a typo?"
|
||||
else
|
||||
flash[:danger] = organization_membership.errors.full_messages
|
||||
end
|
||||
redirect_to internal_user_path(organization_membership.user_id)
|
||||
end
|
||||
|
||||
def destroy
|
||||
organization_membership = OrganizationMembership.find_by(id: params[:id])
|
||||
if organization_membership.destroy
|
||||
flash[:success] = "User was successfully removed from org ##{organization_membership.organization_id}"
|
||||
else
|
||||
flash[:danger] = "Something wen wrong with removing the user from org ##{organization_membership.organization_id}"
|
||||
end
|
||||
redirect_to internal_user_path(organization_membership.user_id)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def organization_membership_params
|
||||
allowed_params = %i[type_of_user user_title organization_id user_id]
|
||||
params.require(:organization_membership).permit(allowed_params)
|
||||
end
|
||||
end
|
||||
|
|
@ -4,8 +4,19 @@ class Internal::ToolsController < Internal::ApplicationController
|
|||
def index; end
|
||||
|
||||
def bust_cache
|
||||
handle_dead_path if params[:dead_link]
|
||||
handle_user_cache if params[:bust_user]
|
||||
flash[:success] = if params[:dead_link]
|
||||
handle_dead_path
|
||||
"#{params[:dead_link]} was successfully busted"
|
||||
elsif params[:bust_user]
|
||||
handle_user_cache
|
||||
"User ##{params[:bust_user]} was successfully busted"
|
||||
elsif params[:bust_article]
|
||||
handle_article_cache
|
||||
"Article ##{params[:bust_article]} was successfully busted"
|
||||
end
|
||||
redirect_to "/internal/tools"
|
||||
rescue StandardError => e
|
||||
flash[:danger] = e
|
||||
redirect_to "/internal/tools"
|
||||
end
|
||||
|
||||
|
|
@ -22,7 +33,7 @@ class Internal::ToolsController < Internal::ApplicationController
|
|||
end
|
||||
|
||||
def handle_article_cache
|
||||
article = User.find(params[:bust_article].to_i)
|
||||
article = Article.find(params[:bust_article].to_i)
|
||||
article.touch(:last_commented_at)
|
||||
CacheBuster.new.bust_article(article)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -37,9 +37,9 @@ class Internal::UsersController < Internal::ApplicationController
|
|||
@user = User.find(params[:id])
|
||||
begin
|
||||
Moderator::ManageActivityAndRoles.handle_user_roles(admin: current_user, user: @user, user_params: user_params)
|
||||
flash[:notice] = "User has been udated"
|
||||
flash[:success] = "User has been updated"
|
||||
rescue StandardError => e
|
||||
flash[:error] = e.message
|
||||
flash[:danger] = e.message
|
||||
end
|
||||
redirect_to "/internal/users/#{@user.id}/edit"
|
||||
end
|
||||
|
|
@ -49,7 +49,7 @@ class Internal::UsersController < Internal::ApplicationController
|
|||
begin
|
||||
Moderator::BanishUser.call_banish(admin: current_user, user: @user)
|
||||
rescue StandardError => e
|
||||
flash[:error] = e.message
|
||||
flash[:danger] = e.message
|
||||
end
|
||||
redirect_to "/internal/users/#{@user.id}/edit"
|
||||
end
|
||||
|
|
@ -58,9 +58,9 @@ class Internal::UsersController < Internal::ApplicationController
|
|||
@user = User.find(params[:id])
|
||||
begin
|
||||
Moderator::DeleteUser.call_deletion(admin: current_user, user: @user, user_params: user_params)
|
||||
flash[:notice] = "@" + @user.username + " (email: " + @user.email + ", user_id: " + @user.id.to_s + ") has been fully deleted. If requested, old content may have been ghostified. If this is a GDPR delete, delete them from Mailchimp & Google Analytics."
|
||||
flash[:success] = "@" + @user.username + " (email: " + @user.email + ", user_id: " + @user.id.to_s + ") has been fully deleted. If requested, old content may have been ghostified. If this is a GDPR delete, delete them from Mailchimp & Google Analytics."
|
||||
rescue StandardError => e
|
||||
flash[:error] = e.message
|
||||
flash[:danger] = e.message
|
||||
end
|
||||
redirect_to "/internal/users"
|
||||
end
|
||||
|
|
@ -70,7 +70,7 @@ class Internal::UsersController < Internal::ApplicationController
|
|||
begin
|
||||
Moderator::MergeUser.call_merge(admin: current_user, keep_user: @user, delete_user_id: user_params["merge_user_id"])
|
||||
rescue StandardError => e
|
||||
flash[:error] = e.message
|
||||
flash[:danger] = e.message
|
||||
end
|
||||
redirect_to "/internal/users/#{@user.id}/edit"
|
||||
end
|
||||
|
|
@ -84,7 +84,7 @@ class Internal::UsersController < Internal::ApplicationController
|
|||
@user.update("#{identity.provider}_username" => nil)
|
||||
flash[:success] = "The #{identity.provider.capitalize} identity was successfully deleted and backed up."
|
||||
rescue StandardError => e
|
||||
flash[:error] = e.message
|
||||
flash[:danger] = e.message
|
||||
end
|
||||
redirect_to "/internal/users/#{@user.id}/edit"
|
||||
end
|
||||
|
|
@ -96,7 +96,7 @@ class Internal::UsersController < Internal::ApplicationController
|
|||
identity = backup.recover!
|
||||
flash[:success] = "The #{identity.provider} identity was successfully recovered, and the backup was removed."
|
||||
rescue StandardError => e
|
||||
flash[:error] = e.message
|
||||
flash[:danger] = e.message
|
||||
end
|
||||
redirect_to "/internal/users/#{@user.id}/edit"
|
||||
end
|
||||
|
|
|
|||
|
|
@ -9,7 +9,9 @@ class SponsorshipDashboard < Administrate::BaseDashboard
|
|||
# on pages throughout the dashboard.
|
||||
ATTRIBUTE_TYPES = {
|
||||
user: Field::BelongsTo,
|
||||
user_id: UserIdField,
|
||||
organization: Field::BelongsTo,
|
||||
organization_id: Field::Number,
|
||||
id: Field::Number,
|
||||
level: Field::String,
|
||||
status: Field::String,
|
||||
|
|
@ -63,8 +65,8 @@ class SponsorshipDashboard < Administrate::BaseDashboard
|
|||
# an array of attributes that will be displayed
|
||||
# on the model's form (`new` and `edit`) pages.
|
||||
FORM_ATTRIBUTES = %i[
|
||||
user
|
||||
organization
|
||||
user_id
|
||||
organization_id
|
||||
level
|
||||
status
|
||||
expires_at
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ class Organization < ApplicationRecord
|
|||
acts_as_followable
|
||||
|
||||
has_many :job_listings
|
||||
has_many :organization_memberships
|
||||
has_many :organization_memberships, dependent: :delete_all
|
||||
has_many :users, through: :organization_memberships
|
||||
has_many :api_secrets, through: :users
|
||||
has_many :articles
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ class ApplicationObserver < ActiveRecord::Observer
|
|||
def warned_user_ping(activity)
|
||||
return unless activity.user.warned == true
|
||||
|
||||
SlackBotPingJob.perform_later message: "Activity: https://dev.to/#{activity.path}\nManage @#{activity.user.username}: https://dev.to/internal/users/#{activity.user.id}",
|
||||
SlackBotPingJob.perform_later message: "Activity: https://dev.to#{activity.path}\nManage @#{activity.user.username}: https://dev.to/internal/users/#{activity.user.id}",
|
||||
channel: "warned-user-comments",
|
||||
username: "sloan_watch_bot",
|
||||
icon_emoji: ":sloan:"
|
||||
|
|
|
|||
38
app/views/internal/users/_user_organizations.html.erb
Normal file
38
app/views/internal/users/_user_organizations.html.erb
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
<div class="row">
|
||||
<h3><u>Organization Memberships</u></h3>
|
||||
<% if @user.organization_memberships.size.positive? %>
|
||||
<ul>
|
||||
<% @user.organization_memberships.includes(:organization).each do |org_membership| %>
|
||||
<li>
|
||||
<u><b><%= org_membership.type_of_user.capitalize %> of:</b></u>
|
||||
<a href="<%= org_membership.organization.path %>" target="_blank" rel="noopener">
|
||||
<%= org_membership.organization.name %>
|
||||
</a>
|
||||
<%= form_for [:internal, org_membership] do |f| %>
|
||||
<%= f.label "Change permission level:" %>
|
||||
<%= f.select("type_of_user", options_for_select(%w[member admin], selected: org_membership.type_of_user)) %>
|
||||
<%= f.submit "Submit", class: "btn btn-primary" %>
|
||||
<% end %>
|
||||
<%= form_for([:internal, org_membership], html: { method: :delete, onsubmit: "return confirm('Are you sure you want to remove them from #{org_membership.organization.name}?)" }) do |f| %>
|
||||
<%= f.submit "Remove from org", class: "btn btn-danger" %>
|
||||
<% end %>
|
||||
</li>
|
||||
<% end %>
|
||||
</ul>
|
||||
<% end %>
|
||||
<h4>Add to New Organization</h4>
|
||||
<ul>
|
||||
<%= form_for [:internal, OrganizationMembership.new] do |f| %>
|
||||
<%= f.hidden_field :user_id, value: @user.id %>
|
||||
<li>
|
||||
Organization ID:
|
||||
<%= f.text_field :organization_id %>
|
||||
</li>
|
||||
<li>
|
||||
Membership Level:
|
||||
<%= f.select(:type_of_user, options_for_select(%w[member admin])) %>
|
||||
<%= f.submit "Add to Org", class: "btn btn-primary" %>
|
||||
</li>
|
||||
<% end %>
|
||||
</ul>
|
||||
</div>
|
||||
|
|
@ -39,8 +39,11 @@
|
|||
<% end %>
|
||||
</div>
|
||||
<%= render "notes" %>
|
||||
<div class="row">
|
||||
<header class="internal-edit-header">
|
||||
<h2>Remove Identity</h2>
|
||||
<button type="button" data-toggle="collapse" data-target="#identity-row">Toggle</button>
|
||||
</header>
|
||||
<div class="row collapse" id="identity-row">
|
||||
<p>Removing a social account identity can solve certain sign in issues, for example:</p>
|
||||
<ul>
|
||||
<li>
|
||||
|
|
@ -66,13 +69,13 @@
|
|||
<%= form_for(@user, url: remove_identity_internal_user_path(@user), html: { method: :delete, onsubmit: "return confirm('Are you sure? This should only be done as a solution for the listed example(s).)" }) do |f| %>
|
||||
<%= f.hidden_field :identity_id, value: identity.id %>
|
||||
<p><b><%= identity.provider.capitalize %> UID: <%= identity.uid %> - Username: <%= identity.auth_data_dump["info"]["nickname"] %></b></p>
|
||||
<%= f.submit "Delete #{identity.provider.capitalize} Identity" %>
|
||||
<%= f.submit "Delete #{identity.provider.capitalize} Identity", class: "btn btn-danger" %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<h3>This should be done only do this if you are certain the user is having the specific problem(s) listed above.</h3>
|
||||
<hr>
|
||||
<h2>Recover a deleted identity:</h2>
|
||||
<% if @user.backup_data %>
|
||||
<% if @user.backup_data.any? %>
|
||||
<hr>
|
||||
<h2>Recover a deleted identity:</h2>
|
||||
<ul>
|
||||
<% @user.backup_data.where(instance_type: "Identity").each do |data| %>
|
||||
<li>
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@
|
|||
<div>Name</div>
|
||||
<div>Twitter</div>
|
||||
<div>GitHub</div>
|
||||
<div>Email</div>
|
||||
</div>
|
||||
|
||||
<% @users.each do |user| %>
|
||||
|
|
@ -36,6 +37,7 @@
|
|||
<div class="grid-item"><%= user.name %></div>
|
||||
<div class="grid-item"><%= user.twitter_username %></div>
|
||||
<div class="grid-item"><%= user.github_username %></div>
|
||||
<div class="grid-item"><%= user.email %></div>
|
||||
</div>
|
||||
<% end %>
|
||||
<%= paginate @users %>
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@
|
|||
<%= render "activity" %>
|
||||
<%= render "credits" %>
|
||||
<%= render "notes" %>
|
||||
<%= render "user_organizations" %>
|
||||
<div class="row">
|
||||
<h2><u>Recent Emails</u></h2>
|
||||
<ul>
|
||||
|
|
|
|||
|
|
@ -63,6 +63,7 @@ Rails.application.routes.draw do
|
|||
post "recover_identity"
|
||||
end
|
||||
end
|
||||
resources :organization_memberships, only: %i[update destroy create]
|
||||
resources :welcome, only: %i[index create]
|
||||
resources :tools, only: %i[index create] do
|
||||
collection do
|
||||
|
|
|
|||
|
|
@ -43,7 +43,12 @@ User.clear_index!
|
|||
token: i.to_s,
|
||||
secret: i.to_s,
|
||||
user: user,
|
||||
auth_data_dump: { "extra" => { "raw_info" => { "lang" => "en" } } },
|
||||
auth_data_dump: {
|
||||
"extra" => {
|
||||
"raw_info" => { "lang" => "en" }
|
||||
},
|
||||
"info" => { "nickname" => user.username }
|
||||
},
|
||||
)
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ RSpec.describe Organization, type: :model do
|
|||
let(:organization) { create(:organization) }
|
||||
|
||||
it { is_expected.to have_many(:sponsorships) }
|
||||
it { is_expected.to have_many(:organization_memberships).dependent(:delete_all) }
|
||||
|
||||
describe "#name" do
|
||||
it "rejects names with over 50 characters" do
|
||||
|
|
|
|||
|
|
@ -193,7 +193,7 @@ RSpec.describe "Internal::Users", type: :request do
|
|||
|
||||
it "expect flash message" do
|
||||
post "/internal/users/#{user.id}/full_delete", params: { user: { ghostify: "false" } }
|
||||
expect(request.flash.notice).to include("fully deleted")
|
||||
expect(request.flash["success"]).to include("fully deleted")
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue