Skip to content

Commit

Permalink
Add new invitation params (forem#20074)
Browse files Browse the repository at this point in the history
* Add new invitation fields

* SMTP enabled check

* Update spec/requests/api/v1/admin/users_spec.rb

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* Add devise_mailer spec

* Add devise_mailer spec

* Update spec/mailers/devise_mailer_spec.rb

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* Update spec/mailers/devise_mailer_spec.rb

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* Update spec/mailers/devise_mailer_spec.rb

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
  • Loading branch information
benhalpern and github-actions[bot] authored Sep 13, 2023
1 parent a679486 commit a079434
Show file tree
Hide file tree
Showing 10 changed files with 179 additions and 12 deletions.
29 changes: 25 additions & 4 deletions app/controllers/admin/invitations_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ def new; end

def create
email = params.dig(:user, :email)
custom_invite_subject = params.dig(:user, :custom_invite_subject)
custom_invite_message = params.dig(:user, :custom_invite_message)
custom_invite_footnote = params.dig(:user, :custom_invite_footnote)

if User.exists?(email: email.downcase, registered: true)
flash[:error] = I18n.t("admin.invitations_controller.duplicate", email: email)
Expand All @@ -23,10 +26,16 @@ def create
end

username = "#{email.split('@').first.gsub(/[^0-9a-z ]/i, '')}_#{rand(1000)}"
User.invite!(email: email,
username: username,
profile_image: ::Images::ProfileImageGenerator.call,
registered: false)
User.invite!({ email: email,
username: username,
profile_image: ::Images::ProfileImageGenerator.call,
registered: false },
nil,
{
custom_invite_subject: custom_invite_subject,
custom_invite_message: custom_invite_message,
custom_invite_footnote: custom_invite_footnote
})
flash[:success] = I18n.t("admin.invitations_controller.create_success")
redirect_to admin_invitations_path
end
Expand All @@ -50,5 +59,17 @@ def resend
end
redirect_to admin_invitations_path
end

def configure_permitted_parameters
devise_parameter_sanitizer.permit(:invite,
keys: %i[email
name
username
custom_invite_subject
custom_invite_message
custom_invite_footnote
profile_image
registered])
end
end
end
8 changes: 7 additions & 1 deletion app/controllers/concerns/api/admin/users_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,13 @@ module UsersController

def create
# NOTE: We can add an inviting user here, e.g. User.invite!(current_user, user_params).
User.invite!(user_params.merge(registered: false))
options = {
custom_invite_subject: params[:custom_invite_subject],
custom_invite_message: params[:custom_invite_message],
custom_invite_footnote: params[:custom_invite_footnote]
}

User.invite!(user_params.merge(registered: false), nil, options)

head :ok
end
Expand Down
9 changes: 9 additions & 0 deletions app/mailers/devise_mailer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,13 @@ def use_settings_general_values
"#{Settings::Community.community_name} <#{ForemInstance.from_email_address}>"
ActionMailer::Base.default_url_options[:host] = Settings::General.app_domain
end

# rubocop:disable Style/OptionHash
def invitation_instructions(record, token, opts = {})
@message = opts[:custom_invite_message]
@footnote = opts[:custom_invite_footnote]
headers = { subject: opts[:custom_invite_subject].presence || "Invitation Instructions" }
super(record, token, opts.merge(headers))
end
# rubocop:enable Style/OptionHash
end
2 changes: 1 addition & 1 deletion app/models/user.rb
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ class User < ApplicationRecord

attr_accessor :scholar_email, :new_note, :note_for_current_role, :user_status, :merge_user_id,
:add_credits, :remove_credits, :add_org_credits, :remove_org_credits, :ip_address,
:current_password
:current_password, :custom_invite_subject, :custom_invite_message, :custom_invite_footnote

acts_as_followable
acts_as_follower
Expand Down
21 changes: 21 additions & 0 deletions app/views/admin/invitations/new.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,27 @@
placeholder: "Email of invitee",
required: true %>
</div>
<div class="crayons-field mt-6">
<%= f.label :custom_invite_subject, "Subject (Optional)", class: "crayons-field__label" %>
<%= f.text_field :custom_invite_subject,
class: "crayons-textfield",
placeholder: 'REPLACES "Invitation Instructions"',
required: true %>
</div>
<div class="crayons-field mt-6">
<%= f.label :custom_invite_message, "Custom Message (Optional)", class: "crayons-field__label" %>
<%= f.text_area :custom_invite_message,
class: "crayons-textfield",
placeholder: "REPLACES INTRO: Hello... You have been invited to join...",
required: true %>
</div>
<div class="crayons-field mt-6">
<%= f.label :custom_invite_footnote, "Custom Footer (Optional)", class: "crayons-field__label" %>
<%= f.text_field :custom_invite_footnote,
class: "crayons-textfield",
placeholder: "Comes right after invitation link",
required: true %>
</div>
<div class="mt-6">
<%= f.submit "Invite User", class: "crayons-btn" %>
</div>
Expand Down
24 changes: 19 additions & 5 deletions app/views/devise/mailer/invitation_instructions.html.erb
Original file line number Diff line number Diff line change
@@ -1,13 +1,27 @@
<p><%= t("devise.mailer.invitation_instructions.hello") %></p>

<p><%= t("devise.mailer.invitation_instructions.someone_invited_you", community_name: Settings::Community.community_name, url: root_url) %></p>
<% if @message.present? %>
<%= Markdown.new(@message).to_html.html_safe %>
<% else %>
<p><%= t("devise.mailer.invitation_instructions.hello") %></p>

<p><%= t("devise.mailer.invitation_instructions.accept_instructions") %>:</p>
<p><%= t("devise.mailer.invitation_instructions.someone_invited_you", community_name: Settings::Community.community_name, url: root_url) %></p>

<p><%= link_to t("devise.mailer.invitation_instructions.accept"), accept_invitation_url(@resource, invitation_token: @token) %></p>
<p><%= t("devise.mailer.invitation_instructions.accept_instructions") %>:</p>

<% end %>

<p><%= link_to t("devise.mailer.invitation_instructions.accept"),
accept_invitation_url(@resource, invitation_token: @token),
style: "color:white; background: #{Settings::UserExperience.primary_brand_color_hex}; border-radius: 8px; padding: 10px 20px; font-size: 1.33em; display: inline-block; margin: 8px 0; text-decoration: none; font-weight: bold;" %></p>

<% if @resource.invitation_due_at %>
<p><%= t("devise.mailer.invitation_instructions.accept_until", due_date: l(@resource.invitation_due_at, format: :"devise.mailer.invitation_instructions.accept_until_format")) %></p>
<% end %>

<p><%= t("devise.mailer.invitation_instructions.ignore") %></p>
<div class="style: font-size: 0.9em">
<% if @footnote.present? %>
<%= Markdown.new(@footnote).to_html.html_safe %>
<% end %>

<p><%= t("devise.mailer.invitation_instructions.ignore") %></p>
</div>
48 changes: 48 additions & 0 deletions spec/mailers/devise_mailer_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,52 @@
end
end
end

describe "#invitation_instructions" do
let(:token) { "some_token" }
let(:custom_invite_message) { "Join our community!!" }
let(:custom_invite_footnote) { "Looking forward to seeing you!!" }
let(:custom_invite_subject) { "You've Been Invited" }

let(:opts) do
{
custom_invite_message: custom_invite_message,
custom_invite_footnote: custom_invite_footnote,
custom_invite_subject: custom_invite_subject
}
end

let(:email) { described_class.invitation_instructions(user, token, opts) }

before do
allow(Settings::SMTP).to receive_messages(
from_email_address: "custom_noreply@example.com",
reply_to_email_address: "custom_reply@example.com",
)
end

it "uses the custom invite subject if provided" do
expect(email.subject).to eq(custom_invite_subject)
end

it "falls back to default subject if custom subject is not provided" do
email_without_custom_subject = described_class.invitation_instructions(user, token, {})
expect(email_without_custom_subject.subject).to eq("Invitation Instructions")
end

it "includes the custom invite message if provided" do
# Ensure your email view actually includes @message
expect(email.to_s).to include(custom_invite_message)
end

it "does not include overridden default message if invite message is provided" do
# Ensure your email view actually includes @message
expect(email.to_s).not_to include("<p>#{I18n.t('devise.mailer.invitation_instructions.accept_instructions')}")
end

it "includes the custom invite footnote if provided" do
# Ensure your email view actually includes @footnote
expect(email.to_s).to include(custom_invite_footnote)
end
end
end
9 changes: 8 additions & 1 deletion spec/mailers/previews/devise_invitable/mailer_preview.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,14 @@
module DeviseInvitable
class MailerPreview < ActionMailer::Preview
def invitation_instructions
Devise.mailer.invitation_instructions(User.last, "faketoken")
user = User.invite!(email: "test@email.com",
username: "username")
options = {
custom_invite_subject: "Custom subject!!",
custom_invite_message: "# Hey!\n\nJoin [our Forem](https://example.com).\n\n## Testing!",
custom_invite_footnote: "Custom footnote! _Yay_"
}
Devise.mailer.invitation_instructions(user, "faketoken", options)
end
end
end
20 changes: 20 additions & 0 deletions spec/requests/admin/invitations_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,26 @@
expect(enqueued_jobs.first[:args]).to match(array_including("invitation_instructions"))
end

it "enqueues an invitation email to be sent with custom subject", :aggregate_failures do
allow(DeviseMailer).to receive(:invitation_instructions).and_call_original

assert_enqueued_with(job: Devise.mailer.delivery_job) do
user_params = { email: "hey#{rand(1000)}@email.co",
custom_invite_subject: "Custom Subject!",
custom_invite_message: "**Custom message**",
custom_invite_footnote: "Custom footnote" }
post admin_invitations_path, params: { user: user_params }
end

expect(DeviseMailer).to have_received(:invitation_instructions) do |_user, _token, args|
expect(args).to include(
custom_invite_subject: "Custom Subject!",
custom_invite_message: "**Custom message**",
)
end
expect(enqueued_jobs.first[:args]).to match(array_including("invitation_instructions"))
end

it "does not create an invitation if a user with that email exists" do
expect do
post admin_invitations_path,
Expand Down
21 changes: 21 additions & 0 deletions spec/requests/api/v1/admin/users_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,27 @@
expect(response).to have_http_status(:ok)
end

it "enqueues an invitation email to be sent with custom options", :aggregate_failures do
allow(DeviseMailer).to receive(:invitation_instructions).and_call_original

assert_enqueued_with(job: Devise.mailer.delivery_job) do
params = { email: "hey#{rand(1000)}@email.co",
custom_invite_subject: "Custom Subject!",
custom_invite_message: "**Custom message**",
custom_invite_footnote: "Custom footnote" }

post api_admin_users_path, params: params, headers: headers
end

expect(DeviseMailer).to have_received(:invitation_instructions) do |_user, _token, args|
expect(args).to include(
custom_invite_subject: "Custom Subject!",
custom_invite_message: "**Custom message**",
)
end
expect(enqueued_jobs.first[:args]).to match(array_including("invitation_instructions"))
end

it "marks user as registered false" do
post api_admin_users_path, params: params, headers: headers

Expand Down

0 comments on commit a079434

Please sign in to comment.