Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add admin invitiations to project #637

Draft
wants to merge 1 commit into
base: add-admin-crud-ui
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -71,3 +71,4 @@ end
gem "tzinfo-data", platforms: [:mingw, :mswin, :x64_mingw, :jruby]

gem "devise", "~> 4.9"
gem "devise_invitable", "~> 2.0"
4 changes: 4 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,9 @@ GEM
railties (>= 4.1.0)
responders
warden (~> 1.2.3)
devise_invitable (2.0.9)
actionmailer (>= 5.0)
devise (>= 4.6)
drb (2.2.1)
erubi (1.13.0)
execjs (2.9.1)
Expand Down Expand Up @@ -411,6 +414,7 @@ DEPENDENCIES
comfortable_mexican_sofa!
cssbundling-rails
devise (~> 4.9)
devise_invitable (~> 2.0)
htmlentities
image_processing (~> 1.13)
jbuilder (~> 2.12)
Expand Down
2 changes: 1 addition & 1 deletion app/models/admin.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@
class Admin < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :registerable, :timeoutable, and :omniauthable
devise :database_authenticatable,
devise :invitable, :database_authenticatable,
:recoverable, :rememberable, :trackable, :validatable
end
2 changes: 1 addition & 1 deletion app/views/admin/admins/index.html.erb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<div class="page-header">
<%= link_to 'New Admin', new_admin_admin_path, class: 'btn btn-secondary float-right' %>
<%= link_to 'Invite New Admin', new_admin_invitation_path, class: 'btn btn-secondary float-right' %>
<h2>Admins</h2>
</div>

Expand Down
49 changes: 49 additions & 0 deletions config/initializers/devise.rb
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,55 @@
# Send a notification email when the user's password is changed.
# config.send_password_change_notification = false

# ==> Configuration for :invitable
# The period the generated invitation token is valid.
# After this period, the invited resource won't be able to accept the invitation.
# When invite_for is 0 (the default), the invitation won't expire.
# config.invite_for = 2.weeks

# Number of invitations users can send.
# - If invitation_limit is nil, there is no limit for invitations, users can
# send unlimited invitations, invitation_limit column is not used.
# - If invitation_limit is 0, users can't send invitations by default.
# - If invitation_limit n > 0, users can send n invitations.
# You can change invitation_limit column for some users so they can send more
# or less invitations, even with global invitation_limit = 0
# Default: nil
# config.invitation_limit = 5

# The key to be used to check existing users when sending an invitation
# and the regexp used to test it when validate_on_invite is not set.
# config.invite_key = { email: /\A[^@]+@[^@]+\z/ }
# config.invite_key = { email: /\A[^@]+@[^@]+\z/, username: nil }

# Ensure that invited record is valid.
# The invitation won't be sent if this check fails.
# Default: false
# config.validate_on_invite = true

# Resend invitation if user with invited status is invited again
# Default: true
# config.resend_invitation = false

# The class name of the inviting model. If this is nil,
# the #invited_by association is declared to be polymorphic.
# Default: nil
# config.invited_by_class_name = 'User'

# The foreign key to the inviting model (if invited_by_class_name is set)
# Default: :invited_by_id
# config.invited_by_foreign_key = :invited_by_id

# The column name used for counter_cache column. If this is nil,
# the #invited_by association is declared without counter_cache.
# Default: nil
# config.invited_by_counter_cache = :invitations_count

# Auto-login after the user accepts the invite. If this is false,
# the user will need to manually log in after accepting the invite.
# Default: true
# config.allow_insecure_sign_in_after_accept = false

# ==> Configuration for :confirmable
# A period that the user is allowed to access the website even without
# confirming their account. For instance, if set to 2.days, the user will be
Expand Down
31 changes: 31 additions & 0 deletions config/locales/devise_invitable.en.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
en:
devise:
failure:
invited: "You have a pending invitation, accept it to finish creating your account."
invitations:
send_instructions: "An invitation email has been sent to %{email}."
invitation_token_invalid: "The invitation token provided is not valid!"
updated: "Your password was set successfully. You are now signed in."
updated_not_active: "Your password was set successfully."
no_invitations_remaining: "No invitations remaining"
invitation_removed: "Your invitation was removed."
new:
header: "Send invitation"
submit_button: "Send an invitation"
edit:
header: "Set your password"
submit_button: "Set my password"
mailer:
invitation_instructions:
subject: "Invitation instructions"
hello: "Hello %{email}"
someone_invited_you: "Someone has invited you to %{url}, you can accept it through the link below."
accept: "Accept invitation"
accept_until: "This invitation will be due in %{due_date}."
ignore: "If you don't want to accept the invitation, please ignore this email. Your account won't be created until you access the link above and set your password."
time:
formats:
devise:
mailer:
invitation_instructions:
accept_until_format: "%B %d, %Y %I:%M %p"
24 changes: 24 additions & 0 deletions db/migrate/20240830185023_devise_invitable_add_to_admins.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# frozen_string_literal: true

class DeviseInvitableAddToAdmins < ActiveRecord::Migration[7.1]
def up
change_table :admins, bulk: true do |t|
t.string :invitation_token
t.datetime :invitation_created_at
t.datetime :invitation_sent_at
t.datetime :invitation_accepted_at
t.integer :invitation_limit
t.references :invited_by, polymorphic: true
t.integer :invitations_count, default: 0
t.index :invitation_token, unique: true # for invitable
t.index :invited_by_id
end
end

def down
change_table :admins, bulk: true do |t|
t.remove_references :invited_by, polymorphic: true
t.remove :invitations_count, :invitation_limit, :invitation_sent_at, :invitation_accepted_at, :invitation_token, :invitation_created_at
end
end
end
13 changes: 12 additions & 1 deletion db/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.

ActiveRecord::Schema[7.1].define(version: 2024_08_14_221626) do
ActiveRecord::Schema[7.1].define(version: 2024_08_30_185023) do
create_table "active_storage_attachments", charset: "utf8mb3", collation: "utf8mb3_general_ci", force: :cascade do |t|
t.string "name", null: false
t.string "record_type", null: false
Expand Down Expand Up @@ -52,7 +52,18 @@
t.string "last_sign_in_ip"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "invitation_token"
t.datetime "invitation_created_at"
t.datetime "invitation_sent_at"
t.datetime "invitation_accepted_at"
t.integer "invitation_limit"
t.string "invited_by_type"
t.bigint "invited_by_id"
t.integer "invitations_count", default: 0
t.index ["email"], name: "index_admins_on_email", unique: true
t.index ["invitation_token"], name: "index_admins_on_invitation_token", unique: true
t.index ["invited_by_id"], name: "index_admins_on_invited_by_id"
t.index ["invited_by_type", "invited_by_id"], name: "index_admins_on_invited_by"
t.index ["reset_password_token"], name: "index_admins_on_reset_password_token", unique: true
end

Expand Down
30 changes: 30 additions & 0 deletions test/integration/admin_invitation_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# frozen_string_literal: true

require "test_helper"

class AdminInvitationTest < ActionDispatch::IntegrationTest
setup do
@admin = admins(:admin)
end

test "not able to invite other admins when logged out" do
get new_admin_invitation_path

assert_redirected_to new_admin_session_path
end

test "able to invite other admins when admin" do
sign_in @admin

get new_admin_invitation_path

assert_response :success
end

# TODO: Add more tests here for the basic functionality:
# test "create an admin invitation" do
# end

# test "accepting an admin invitation" do
# end
end
Loading