Active Admin rails, new button to an existing action - ruby-on-rails

I'm trying add a new button(Resend Email) to an existing(Send Email) method/route on Active Admin rails. I have done these kind of tasks on a normal rails with MVC architecture. Where I used to add the required route path in a html.erb file. I'm finding it quiet difficult to implement the same on Active Admin since it doesn't have that MVC look.
On routes.rb file I have
routes.rb
Rails.application.routes.draw do
mount Bootsy::Engine => '/bootsy', as: 'bootsy'
devise_for :admin_users, ActiveAdmin::Devise.config
ActiveAdmin.routes(self)
devise_for :users,controllers: {registrations: 'users/registrations',sessions: 'users/sessions',confirmations: 'users/confirmations'}
end
Here confirmations: 'users/confirmations' is used to send Email Verfication mail to the users. That's working fine. Some users forget to look into their email. So I'm trying to create a "Resend Email" button on the ActiveAdmin file, which on submitting sends the verification email mail again.
What path should I set inorder to send the mail again. I'm new to active-admin rails. Your help would be much appreciated.
User.rb
ActiveAdmin.register User, :as => 'User' do
action_item only: :show do
link_to('Resend Email', )
end
end
user_mailer.rb
def confirmation_instructions(record, token, opts={})
#system_email = SystemEmail.find_by(title: 'Email Verification')
#subject = #system_email.try(:subject).to_s
#subject = "Email Verification" if #subject.blank?
opts = {subject: #subject}
#token = token
devise_mail(record, :confirmation_instructions, opts)
end

Related

Rails: No route found when JWT token use in URL

I have a route with my JWT token as a path variable but it doesn't work with the JWT token eyJhbGciOiJub25lIn0.eyJjb25maXJtYXRpb25fc2VudF9hdCI6IjIwMTgtMDEtMjQgMDY6MDQ6MzEgKzEzMDAiLCJleHBpcmVzX2F0IjoiMjAxOC0wMS0yNyAwNjowNDozMSArMTMwMCIsInVzZXJfaWQiOjM5fQ.
When I switch the JWT token to something less complicated e.g. 5 then the route works. I'm guessing that the format of the JWT need some special treatment in the rails router?
get '/verify/:jwt', to: 'users#verify_email'
No route matches [GET] "/verify/eyJhbGciOiJub25lIn0.eyJjb25maXJtYXRpb25fc2VudF9hdCI6IjIwMTgtMDEtMjQgMDY6MDQ6MzEgKzEzMDAiLCJleHBpcmVzX2F0IjoiMjAxOC0wMS0yNyAwNjowNDozMSArMTMwMCIsInVzZXJfaWQiOjM5fQ
routes.rb
Rails.application.routes.draw do
extend DeviseRoutes
extend PageRoutes
# Root route
root to: "pages#home"
end
devise_routes.rb
module DeviseRoutes
def self.extended(router)
router.instance_exec do
devise_for :users, path: '', path_names: {
sign_in: 'login',
sign_out: 'logout',
sign_up: 'register',
edit: '/user/edit'
}, controllers: { registrations: 'users' }
# User profile management
devise_scope :user do
get '/profile/:id', to: 'users#profile_home', as: 'profile'
# Verify email
get '/verify', to: 'users#verify_email'
end
end
end
end
users_controller
class UsersController < Devise::RegistrationsController
include AuthenticationConcern
require 'utilities/custom_mailer'
require 'jwt'
def profile_home
#user_id = params[:id]
check_user_route_access current_user, #user_id
#user = User.includes(:skills).find_by(id: #user_id)
#skills = #user.skills.eager_load(:profession)
end
def create
super
if current_user
CustomMailer.send_initial_user_signup(user_id: current_user.id,
to_email: current_user.email,
user_full_name: current_user.full_name)
end
end
def verify_email
jwt_token = params[:token]
#jwt_token_decoded = true
# make sure the jwt_token can be decoded, if not crash softly via
# error on the page rather than hard crash
begin
decoded = (JWT.decode jwt_token, nil, false)[0]
rescue
#jwt_token_decoded = false
end
if #jwt_token_decoded
decoded_expires_at = decoded["expired_at"]
user_id = decoded["user_id"]
#user = User.find_by(id: user_id)
# 1. if user is verified, redirect to login page
if #user != nil and #user.confirmed_at != nil
# flash[:success] = t('successfully_created')
redirect_to new_user_session_path
end
# 2. once verified, provide option in view to go to login page
end
# render verification page
end
end
Does anyone have any suggestions?
In your route the JWT consists of various special characters like '.' because of which the rails router is unable to route it properly.
Solution :-
1:- Use route globbing using wildcard segments
Example :-
Change you route to this
get '/verify/*jwt', to: 'users#verify_email', constraints: { jwt: /.*/ }
This will give params[:jwt] = "passed jwt in url"
You can customize the regex used here to make it more meaningful for jwt token which consists of two '.' and other special characters.
Read this for more information :- Rails route globbing and wildcard segments
The most probable cause of this issue is that you have another route before than get '/verify/:jwt', to: 'users#verify_email'. Rails Router gives priority to the first instance it finds.
So if for example, your routes.rb looks like this:
resources :verify # this may not be your case, just an example
get '/verify/:jwt', to: 'users#verify_email'
In this case, rails will ignore get line, and whenever you GET /verify/<anything> will be routed to verify#show
On the other hand, if you swap those lines like this,
get '/verify/:jwt', to: 'users#verify_email'
resources :verify # this may not be your case, just an example
Then every GET /verify/<anything> will be routed to verify_email.

How to create new edit forms in Devise?

Right now Devise generated edit.html.erb, which is great for editing essential info. However, I want to generate a new edit page, similar to edit.html.erb, but there, users will be able to edit other information about themselves.. I don't want to put everything on edit.html.erb because of UI.
So far I only added new columns to users table for a new edit page that I'm going to create.
Right now these are my routes:
devise_for :users, class_name: 'FormUser', :controllers => { omniauth_callbacks: 'omniauth_callbacks', registrations: 'registrations' }
root 'setup#index'
get '/setup' => 'setup#index'
get '/users/sign_out' => 'setup#index'
And this is my registration_controller.rb:
class RegistrationsController < Devise::RegistrationsController
def update_resource(resource, params)
if resource.encrypted_password.blank? # || params[:password].blank?
resource.email = params[:email] if params[:email]
if !params[:password].blank? && params[:password] == params[:password_confirmation]
logger.info "Updating password"
resource.password = params[:password]
resource.save
end
if resource.valid?
resource.update_without_password(params)
end
else
resource.update_with_password(params)
end
end
end
So I created a new controller with views but I have no idea what to put now in the routes.
Name of the new controller is styles_controller.rb and and views are fits.html.erb..
This script will generate controllers for you to override the behavior of Devise (but I highly recommend you do not do that):
https://github.com/plataformatec/devise/wiki/Tool:-Generate-and-customize-controllers
This shows you how to generate the view templates for you to override the default screens to login/etc.
https://github.com/plataformatec/devise#configuring-views
And this tutorial shows how to allow users to edit their profile without a password:
https://github.com/plataformatec/devise/wiki/How-To%3a-Allow-users-to-edit-their-account-without-providing-a-password

Rails 4 devise_invitable "The invitation token provided is not valid!" error

I have been following Ryan Boland's Rails multitenancy tutorial, but have run into a snag with devise_invitable.
I create a new account and user/account owner on a chosen subdomain (mysubdomain.lvh.me:3000), from which I can send a user invitation just fine. I open the invitation link in an incognito Chrome session to ensure I am not logged in or have any current session. Upon clicking on the invitation link, I am redirected to the sign in page (mysubdomain.lvh.me:3000/users/sign_in) and see a flash notice: "The invitation token provided is not valid!"
Related to this one:
Rails 4 devise_invitable invitation token invalid
[SOLVED]
In case anyone has the same issue, override the invitations controller and change the Tenant with Apartment:
# app/controllers/users/invitations_controller.rb
class Users::InvitationsController < Devise::InvitationsController
private
def resource_from_invitation_token
Apartment::Tenant.switch!(request.subdomain) //ADD THIS BABY!
unless params[:invitation_token] &&
self.resource = resource_class.find_by_invitation_token(params[:invitation_token], true)
set_flash_message(:alert, :invitation_token_invalid)
redirect_to after_sign_out_path_for(resource_name)
end
end
end
Remember to update your routes also, like this:
# config/routes.rb
devise_for :users, :controllers => { :invitations => 'users/invitations' }

Allowing admins to add users with Devise

I'm trying to make it so only admins can add uses with devise. I've gotten it mostly working however now when I'm logged in as an admin and submit the sign up form it kicks me back with the error: You are already signed in.
I've tried to follow the instructions here: http://wiki.summercode.com/rails_authentication_with_devise_and_cancan but it doesn't seem to mention this situation.
Do I need to do further overriding in the editors_controller to allow this?
Here are my routes ("editors" is the name of my user model):
devise_for :admins, :skip => [:registrations]
as :admin do
get 'admin/editors' => 'editors#index', as: :admin_editors
get 'admin/editors/new' => 'editors#new', as: :new_editor
delete 'admin/editors/:id' => 'editors#destroy', as: :destroy_editor
end
devise_for :editors, :skip => [:registrations], :controllers => { :registrations => "editors" }
and my editors_controller in "app/controllers/"
class EditorsController < Devise::RegistrationsController
before_filter :check_permissions, :only => [:new, :create, :cancel]
skip_before_filter :require_no_authentication
def dashboard
render "editors/dashboard.html.haml"
end
def index
#editors = Editor.all
respond_to do |format|
format.html
end
end
private
def check_permissions
authorize! :create, resource
end
end
EDIT
I noticed this Processing by Devise::RegistrationsController#create as HTML in the logs when I submit the form. I had suspected that perhaps the skip_before_filter :require_no_authentication wasn't being called, but assumed that because the EditorsController was inheriting from RegistrationController that before filter would work properly. Is that not the case?
You'll want to implement your own create method on EditorsController instead of inheriting that action from Devise::RegistrationsController. As you're seeing, the method in Devise::RegistrationsController will first check to see if you're already logged in and kick you back if you are. If you're not logged in it will create a User account and then log you in as that user.
You're trying to get around that problem with skip_before_filter :require_no_authentication, but it's likely that your form is POSTing to /editors instead of /admin/editors. So, you'll need to add a route that allows you to get to create on the EditorsController :
as :admin do
post 'admin/editors' => 'editors#create'
# your other :admin routes here
end
Then you'd want to implement a scaled down version of create. You probably want something kind of like this :
class EditorsController < Devise::RegistrationsController
def create
build_resource(sign_up_params)
if resource.save
redirect_to admin_editors_path
else
clean_up_passwords resource
respond_with resource
end
end
# your other methods here
end
You'll also want to make sure that the admin/editors/new template is pointing the form to the correct route ('admin/editors').
None of the googleable solutions worked when I tried them. This works
What I did was create a new action in the controller and a new route for it, and connect the links on my views that normally connect to create to now call my route and action.
But that wasn't enough. Because Devise is listening and will grab any add you try to do and validate it through it's own code. So instead I just add the new user record with a sql insert.
Add this route
post 'savenew', to: 'users#savenew'
Add this action to the user controller:
def savenew
rawsql = "insert into users (email, created_at,updated_at) values ('#{user_params[:email]}',now(), now())"
sql = rawsql
ActiveRecord::Base.connection.execute(sql)
redirect_to action: 'index''
end
View: new.html.erb
change the form_for so that submit will go to the new route and action, not the default Rails one.
<%= form_for User, :url => {:action => "savenew"} do |f| %>
Using Rails 4.2.6 here (my model is User instead of Editor). The following solution bypasses (I think) any devise actions that may interfere with new User creation by the admin:
Add this action to the Users controller:
def savenew
User.create_new_user(user_params)
redirect_to action: 'index'
end
Add this private method to the Users controller if it does not exist:
private
def user_params
params.require(:user).permit(:email, :password,
:password_confirmation)
end
Add this to config/routes.rb:
match '/savenew', to: 'users#savenew', via: :post
Add this class method to the User model:
def self.create_new_user(params)
#user = User.create!(params)
end
I don't have a separate Admin class in my application. Instead, I defined an admin attribute for Users and check for it with a :validate_admin before_action filter in the UsersController.
I wanted to be able to create a new user from the :index view, so I added a button:
<%= button_to 'New User', '/new_user', class: 'btn btn-primary',
method: :get %>
You might have to tweak the above solution if you have any after_create actions in the User model (e.g. sending a welcome email).

How can I customize Devise's "resend confirmation email"

I have a custom mailer (UserMailer.rb) and a few methods to override the default Devise methods for the welcome email and forgot password emails. The mailer uses a custom template to style the emails--and it works great.
In config/initializers, I have a file with
module Devise::Models::Confirmable
# Override Devise's own method. This one is called only on user creation, not on subsequent address modifications.
def send_on_create_confirmation_instructions
UserMailer.welcome_email(self).deliver
end
...
end
(Again, UserMailer is setup and works great for the welcome email and reset password email.)
But what's not working is the option to "Resend confirmation instructions." It sends with the default Devise styling and I want it to use the styling of my mailer layout. I know I can manually add the layout to the default Devise layout, but I'd like to keep DRY in effect and not have to do that.
I've tried overriding the send_confirmation_instructions method found here, but I'm getting a wrong number of arguments (1 for 0) error in create(gem) devise-2.2.3/app/controllers/devise/confirmations_controller.rb at
7 # POST /resource/confirmation
8 def create
9 self.resource = resource_class.send_confirmation_instructions(resource_params)
In my initializer file, I'm able to get to this error by adding a new override for Devise, but I'm probably not doing this correctly:
module Devise::Models::Confirmable::ClassMethods
def send_confirmation_instructions
UserMailer.send_confirmation_instructions(self).deliver
end
end
Any ideas?
You don't have to go through that initializer to do that. I've done this by overriding the confirmations controller. My routes for devise look like:
devise_for :user, :path => '', :path_names => { :sign_in => 'login', :sign_out => 'logout', :sign_up => 'signup'},
:controllers => {
:sessions => "sessions",
:registrations => "registrations",
:confirmations => "confirmations"
}
Then, create the confirmations_controller and extend the Devise::ConfirmationsController to override:
class ConfirmationsController < Devise::ConfirmationsController
In that controller, I have a create method to override the default:
def create
#user = User.where(:email => params[:user][:email]).first
if #user && #user.confirmed_at.nil?
UserMailer.confirmation_instructions(#user).deliver
flash[:notice] = "Set a notice if you want"
redirect_to root_url
else
# ... error messaging or actions here
end
end
Obviously, in UserMailer you can specify the html/text templates that will be used to display the confirmation message. confirmation_token should be a part of the #user model, you can use that to create the URL with the correct token:
<%= link_to 'Confirm your account', confirmation_url(#user, :confirmation_token => #user.confirmation_token) %>

Resources