Keeping a query-string in devise sign up - ruby-on-rails

I am using rails with devise for signing up. Also I added an invite code, so not everybody can sign up.
The invite code gets transmitted via query-string like "/users/sign_up?invite_code=wajdpapojapsd" and gets added to a hidden field of the sign-up form with "f.hidden_field :invite_code, :value => params[:invite_code]".
This works pretty well. The only problem is that if the sign up doesn't get validated and rejected, devise redirects to "/users" and loses the query string with the invite_code in it.
As the email stays in the sign up form after the failed attempt, I believe that this should also work for the invite code. As a worst case solution redirecting :back after a failed sign up and losing the email, but keeping the invite code, would be better than the way it works now.
EDIT: By now I ve set up a registration controller for devise, but don't have any idea how to get the desired behavior.
Any help on how to keep the query string or just the invite code would be awesome.

I found a working solution by now.
I used jstr's answer to set up the controller for devise and added the "session" line.
class MyRegistrationsController < Devise::RegistrationsController
prepend_view_path "app/views/devise"
def create
super
session[:invite_code] = resource.invite_code
end
def update
super
end
end
Afterwards I added following to the devise/registrations/new.html.erb
<% if params[:invite_code]
#invite_code = params[:invite_code]
else
#invite_code = session[:invite_code]
end %>
And changed the hidden_field to
<%= f.hidden_field :invite_code, :value => #invite_code %>

You might need to make your own subclassed Devise controllers to get this to work.
This answer has a good description of how to to this.
The basics:
Install the Devise views if you haven't already with rails generate devise:views
Create a subclassed Devise::RegistrationsController
Update your Devise routes declaration to get Devise to use your subclassed controller

#James Lever 's solution has one disadvantage. invite_code remains in session. In some solutions it can cause problems. The alternative soultion would be:
def create
# set invite code to session:
session[:invite_code] = resource.invite_code
# here the form is rendered and invite code from session would be used:
super
# delete invite code from session:
session.delete('invite_code')
end

why don't use instance variable such
#invite_code = params[:invite_code]
Then when a sign up didn't pass validation, you can use that variable in the view which will be displayed for failed sign up.
I mean before, it's redirected, you should keep the invite code parameters with instance variable. sorry if i'm mistaken.

Related

code reusability issue in active admin rails

How to avoid common code used in active admin action item and controller action.
I have seen people used to write the same code in the controller and active admin action item.
Is there any way to avoid it?
Example is like i want to cancel a user and it requires 3-4 steps to do it.
So i have writtent this code in users controller.
Now i have resource user in active admin and i want to delete the user from active admin. I have created an action item and again written the same code of deleting the user in the member action.
Is there any way to avoid above.
If you want to share code between different controllers, you should look to the rails concerns https://api.rubyonrails.org/v5.2.2/classes/ActiveSupport/Concern.html
Assuming this is in the User register block (probably at app/admin/users.rb) this might help you. The action_item only contains a link (actually a form that posts) to the actual member_action. This is just example code:
action_item :cancel_user, :only => :edit do
link_to 'Cancel user', do_cancel_user_admin_user_path(resource), method => :post
end
member_action :do_cancel_user, :method => :post do
flash.notice = "User will be canceled"
resource.cancel # I guess this would the 4 lines of code that you are repeating
redirect_to edit_admin_user_path(resource) and return
end
Let me know if this did/did not confuse you. Good luck!
P.S. A few weeks ago someone asked something similar, this might also be of help: How to reset user´s password by Devise authentication token directly from edit_page in Active Admin?

Accessing Doorkeeper Application Information before login redirect

Explanation
I am wanting to halt the authorization process of a client app (running OAuth2) coming to the parent app (running Doorkeeper) in order to see which client app is requesting a login. That way I can then look up the clientID and dynamically build a custom login screen for the client app. Right now, my client goes to parent, AuthorizationController is called, but before new is called and I can get the params[:client_id], authenticate_resource_owner! is called with a before_action. That then sends the user to the login page if they are not already logged in with the parent. So, before I can get the param, it is being redirected.
Question
The authenticate_resource_owner! is held in a Doorkeeper helper file. I thought that I set it up correctly to bypass the default helper and go to mine where I can try and grab the param and save in sessions before the redirect, but I guess my route is not set up correctly and I can't find any documentation on how to correctly call it. Can anyone help?
Code
Code for setting up the client:
def setup_client
#client = Application.find_by(uid: params[:client_id])
session[:client_name] = #client.name
authenticate_resource_owner!
end
I know that the first 2 lines work as I placed them in the CustomAuthorizationsController with a byebug and it triggered after the login and before redirect back to client and showed the client name stored in a session variable.
In my config/routes.rb
use_doorkeeper do
controllers :applications => 'doorkeeper/custom_applications'
controllers :authorizations => 'doorkeeper/custom_authorizations'
helpers :doorkeeper => 'doorkeeper/doorkeeper'
end
Helper file is located in app/helpers/doorkeeper/doorkeeper_helper.rb
Error
When I start up my server I get:
: from ~/ruby-2.5.0/gems/doorkeeper-5.0.2/lib/doorkeeper/rails/routes/mapper.rb:12:in `instance_eval'
~/settingsParentApp/config/routes.rb:65:in `block (2 levels) in <top (required)>': undefined method `helpers' for #<Doorkeeper::Rails::Routes::Mapper:0x00007ffd539b9c10> (NoMethodError)
Conclusion
Am I even doing this right? Is there a simpler way built into Doorkeeper that I am not seeing to get this information to customize the login screen? Or is there some error that I am not seeing in how I am calling the helper file?
After thinking through my problem in order to ask this question, a solution dawned on me. I tested it out and it worked. I forgot that in a controller, the before_action statements are called in the order they are presented. So, my solution was just to reorder my statements to call the setup_client first before the authenticate_resource_owner!. This set up the session variable before redirecting to the login screen and then allowed me to have the variable available for use.
Code
Within my config/routes.rb file:
use_doorkeeper do
controllers :applications => 'doorkeeper/custom_applications'
controllers :authorizations => 'doorkeeper/custom_authorizations'
end
This custom route bypasses the doorkeeper default authorization controller and goes to a custom one which inherits from the default controller. So, all I need within this custom one is this code:
Found: app/controllers/doorkeeper/custom_authorizations_controller.rb
module Doorkeeper
class CustomAuthorizationsController < Doorkeeper::AuthorizationsController
before_action :setup_client
before_action :authenticate_resource_owner!
def setup_client
#client = Application.find_by(uid: params[:client_id])
session[:client_name] = #client.name
end
end
end
This code is then run before it looks to the Doorkeeper's default AuthorizationsController and thus calls setup_client first. The session variable is then saved and in the login screen I can call it this way:
<%
if session[:client_name].nil?
#client_name = ''
else
#client_name = ' for ' + session[:client_name]
end
#page_name = "Login" + #client_name
%>
And then in header of the page I call this within the HTML:
<h1><%= #page_name %></h1>
I may do more fancy things later, like saving client icons/logos and color schemes to make branding specific on the login page, but for now, this basic issue has been resolved. Thank you all for acting as my sounding board and problem-solving ducks... if you know of that reference. :-) Happy Coding!

'Cannot redirect to nil' error after adding a field to devise registration view

After adding this line to the devise/registrations/new.html.haml file (view):
%div
= f.label :account_type
%br/
= f.select(:account_type, [["Usertype1","usertype1"],["Usertype2","usertype2"]], {:selected => nil, :prompt => 'Pick One'})
I get the following error after clicking on the confirmation link in the confirmation e-mail:
ActionController::ActionControllerError in Devise::ConfirmationsController#show
Cannot redirect to nil!
It only happens if I select Usertype2 upon registration. I also made the account_type attr_accessible. The account_type seems to be getting assigned (I checked in the rails console) and the development logs don't have any further information.
I think this is the line in the devise confirmations controller where the error is occurring:
respond_with_navigational(resource){ redirect_to after_confirmation_path_for(resource_name, resource) }
Also, the account is being confirmed, but when trying to log in, I get the following:
undefined method `user_url' for #<Devise::SessionsController:0x9d1659c>
which is in the create action of the devise sessions controller.
Any help would be appreciated. Thanks!
John
The two errors you mentioned are one and the same, essentially when you are signing-in successfully Devise is unable to detect where to redirect you. This problem often occurs when you have multiple models or try to setup a custom redirect (post sign in) in the routes file.
Try to define the path in the ApplicationController.
Devise docs say that the after_sign_in_path_for method takes the actual model object (ie: the model being signed-in)
def after_sign_in_path_for(resource)
signed_in_path_for_user
end
Note: You can do the same for several Devise paths / variables (override them). Also for more information on doing this for multiple Devise models in the same app, you can look at this question and it's answer.

Devise, allowing users to change passwords

I have an application that has a custom registration controller, called users_controller, for devise because only Admin is able to create users. I want to allow users to change their password by entering their old password and entering a new one and I found: https://github.com/plataformatec/devise/wiki/How-To:-Allow-users-to-edit-their-password but it does not give enough detail.
What do I need to put in my routes?
Where and what do I need to put in my views?
If you have a devise model User, you can use this path to route to the change password page.
edit_user_registration_path
Just use this in your view as:
<%= link_to 'Change Password', edit_user_registration_path %>
This should do the trick, it works for me.
I have found the soulution and have fixed this on the Devise wiki
This is not enough, tough. You'll have to add attr_accessor :current_password to the model, like explained in here.

Devise. Registration and Login at the same page

I'm trying to integrate Devise into my application. I need implement login form at top of the page (I've implemented this form into layout page) and I've implemented registration which contains registration form.
But it shows validation errors for both form when I tried submit incorrect registration data.
Without more information, it's hard to guess what the problem is. I've found the Wiki pages to be really helpful (and increasingly so), though you may have already looked them over:
Devise Wiki Pages
Two pages that might be relevant to your needs:
Display a custom sign_in form anywhere in your app
Create custom layouts
Hope this helps!
-- ff
The problem of seeing the validation errors for both forms stems from the 2 things. First, devise forms use a generic 'resource' helper. This creates a User object, and that same user objet gets used for both the sign up and the sign in form. Second, devise errors are typically displayed using the 'devise_error_messages!' helper which uses that same shared resource.
To have sign in and sign up on the same page you need to create different user objects for each form, and a new way of displaying the error messages.
First off, you'll need to create your own registration controller (in app/controllers/users/)
class Users::RegistrationsController < Devise::RegistrationsController
include DevisePermittedParameters
protected
def build_resource(hash=nil)
super
# Create an instance var to use just for the sign up form
#sign_up_user = self.resource
end
end
And update your routes file accordingly
devise_for :users, controllers: {
registrations: 'users/registrations'
}
Next you'll need your own error messages and resource helpers. Create a new helper like devise_single_page_helper.rb and add the following:
module DeviseSinglePageHelper
def devise_error_messages_for_same_page(given_resource)
return "" if given_resource.errors.empty?
messages = given_resource.errors.full_messages.map { |msg| content_tag(:li, msg) }.join
sentence = I18n.t("errors.messages.not_saved",
count: given_resource.errors.count,
resource: given_resource.class.model_name.human.downcase)
html = <<-HTML
<div id="error_explanation">
<h2>#{sentence}</h2>
<ul>#{messages}</ul>
</div>
HTML
html.html_safe
end
def sign_up_user
#sign_up_user ||= User.new(username: 'su')
end
def sign_in_user
#sign_in_user ||= User.new(username: 'si')
end
end
Finally, in your views, update your forms like so:
-# The sign up form
= simple_form_for(sign_up_user, url: registration_path(resource_name)) do |f|
-#...
= devise_error_messages_for_same_page(sign_up_user)
-# The sign in form
= simple_form_for(sign_in_user, url: sessions_path(resource_name)) do |f|
#...
= devise_error_messages_for_same_page(sign_in_user)
All of this together gives you 2 different objects - 1 for sign up and 1 for sign in. This will prevent the error messages from one showing in the other. Please note that recommend putting both forms on your sign in page (and perhaps having the default sign up page redirect to the sign in page) because by default a failed sign in attempt will redirect to the sign in page.
You should have two forms on the page — one for signing up and one for registering. If you want a single form and multiple potential actions you are going to need a couple buttons that get handled client side and change the form's action & method to the appropriate route depending you want to create a user or a session.
If you think you did this already, the problem almost certainly lies in your code. If you were to share it with us we could perhaps point out something you may have missed.

Resources