-
Notifications
You must be signed in to change notification settings - Fork 2.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Initial pass at household invites * Invitee setup * Clean up add member form * Lint and other tweaks * Security cleanup * Lint * i18n fixes * More i18n cleanup * Show pending invites * Don't use turbo on the form * Improved email design * Basic tests * Lint * Update onboardings_controller.rb * Registration + invite cleanup * Lint * Update brakeman.ignore * Update brakeman.ignore * Self host invite links * Test tweaks * Address missing param error
- Loading branch information
Showing
26 changed files
with
502 additions
and
45 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
class InvitationsController < ApplicationController | ||
skip_authentication only: :accept | ||
def new | ||
@invitation = Invitation.new | ||
end | ||
|
||
def create | ||
unless Current.user.admin? | ||
flash[:alert] = t(".failure") | ||
redirect_to settings_profile_path | ||
return | ||
end | ||
|
||
@invitation = Current.family.invitations.build(invitation_params) | ||
@invitation.inviter = Current.user | ||
|
||
if @invitation.save | ||
InvitationMailer.invite_email(@invitation).deliver_later unless self_hosted? | ||
flash[:notice] = t(".success") | ||
else | ||
flash[:alert] = t(".failure") | ||
end | ||
|
||
redirect_to settings_profile_path | ||
end | ||
|
||
def accept | ||
@invitation = Invitation.find_by!(token: params[:id]) | ||
|
||
if @invitation.pending? | ||
redirect_to new_registration_path(invitation: @invitation.token) | ||
else | ||
raise ActiveRecord::RecordNotFound | ||
end | ||
end | ||
|
||
private | ||
|
||
def invitation_params | ||
params.require(:invitation).permit(:email, :role) | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,7 @@ | ||
class Settings::ProfilesController < SettingsController | ||
def show | ||
@user = Current.user | ||
@users = Current.family.users.order(:created_at) | ||
@pending_invitations = Current.family.invitations.pending | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
module InvitationsHelper | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
class InvitationMailer < ApplicationMailer | ||
def invite_email(invitation) | ||
@invitation = invitation | ||
@accept_url = accept_invitation_url(@invitation.token) | ||
|
||
mail( | ||
to: @invitation.email, | ||
subject: t(".subject", inviter: @invitation.inviter.display_name) | ||
) | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
class Invitation < ApplicationRecord | ||
belongs_to :family | ||
belongs_to :inviter, class_name: "User" | ||
|
||
validates :email, presence: true, format: { with: URI::MailTo::EMAIL_REGEXP } | ||
validates :role, presence: true, inclusion: { in: %w[admin member] } | ||
validates :token, presence: true, uniqueness: true | ||
validate :inviter_is_admin | ||
|
||
before_validation :generate_token, on: :create | ||
before_create :set_expiration | ||
|
||
scope :pending, -> { where(accepted_at: nil).where("expires_at > ?", Time.current) } | ||
scope :accepted, -> { where.not(accepted_at: nil) } | ||
scope :most_recent_for_email, ->(email) { where(email: email).order(accepted_at: :desc).first } | ||
|
||
def pending? | ||
accepted_at.nil? && expires_at > Time.current | ||
end | ||
|
||
private | ||
|
||
def generate_token | ||
loop do | ||
self.token = SecureRandom.hex(32) | ||
break unless self.class.exists?(token: token) | ||
end | ||
end | ||
|
||
def set_expiration | ||
self.expires_at = 3.days.from_now | ||
end | ||
|
||
def inviter_is_admin | ||
inviter.admin? | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
<h1><%= t(".greeting") %></h1> | ||
|
||
<p> | ||
<%= t(".body", | ||
inviter: @invitation.inviter.display_name, | ||
family: @invitation.family.name).html_safe %> | ||
</p> | ||
|
||
<%= link_to t(".accept_button"), @accept_url, class: "button" %> | ||
|
||
<p class="footer"><%= t(".expiry_notice", days: 3) %></p> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
<%= modal_form_wrapper title: t(".title"), subtitle: t(".subtitle") do %> | ||
<%= styled_form_with model: @invitation, class: "space-y-4", data: { turbo: false } do |form| %> | ||
<%= form.email_field :email, | ||
required: true, | ||
placeholder: t(".email_placeholder"), | ||
label: t(".email_label") %> | ||
<%= form.select :role, | ||
options_for_select([ | ||
[t(".role_member"), "member"], | ||
[t(".role_admin"), "admin"] | ||
]), | ||
{}, | ||
{ label: t(".role_label") } %> | ||
|
||
<div class="w-full"> | ||
<%= form.submit t(".submit"), class: "bg-gray-900 text-white rounded-lg px-4 py-2 w-full" %> | ||
</div> | ||
<% end %> | ||
<% end %> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,20 +1,35 @@ | ||
<% | ||
header_title t(".title") | ||
header_title @invitation ? t(".join_family_title", family: @invitation.family.name) : t(".title") | ||
%> | ||
<% if self_hosted_first_login? %> | ||
<div class="fixed inset-0 w-full h-fit bg-gray-25 p-5 border-b border-alpha-black-200 flex flex-col gap-3 items-center text-center mb-12"> | ||
<h2 class="font-bold text-xl"><%= t(".welcome_title") %></h2> | ||
<p class="text-gray-500 text-sm"><%= t(".welcome_body") %></p> | ||
</div> | ||
<% elsif @invitation %> | ||
<div class="space-y-1 mb-6 text-center"> | ||
<p class="text-gray-500"> | ||
<%= t(".invitation_message", | ||
inviter: @invitation.inviter.display_name, | ||
role: t(".role_#{@invitation.role}")) %> | ||
</p> | ||
</div> | ||
<% end %> | ||
<%= styled_form_with model: @user, url: registration_path, class: "space-y-4" do |form| %> | ||
<%= form.email_field :email, autofocus: false, autocomplete: "email", required: "required", placeholder: "[email protected]", label: true %> | ||
<%= form.email_field :email, | ||
autofocus: false, | ||
autocomplete: "email", | ||
required: "required", | ||
placeholder: "[email protected]", | ||
label: true, | ||
disabled: @invitation.present? %> | ||
<%= form.password_field :password, autocomplete: "new-password", required: "required", label: true %> | ||
<%= form.password_field :password_confirmation, autocomplete: "new-password", required: "required", label: true %> | ||
<% if invite_code_required? %> | ||
<% if invite_code_required? && !@invitation %> | ||
<%= form.text_field :invite_code, required: "required", label: true, value: params[:invite] %> | ||
<% end %> | ||
<%= form.hidden_field :invitation, value: @invitation&.token %> | ||
<%= form.submit t(".submit") %> | ||
<% end %> |
Oops, something went wrong.