Two different login pages for one model with devise on rails - ruby-on-rails

I am working on an rails app in which there is one table Users, which has two roles Admin and member. Until now I am using single login page.
But now, I need to make two different login pages with completely different styling for admin and member.
Moreover Admin don't have sign_up option but member has.
I am totally confused that is it possible or not?
If possible, how to achieve this.

Devise sign in form is just a form with action matching to devise controller.
You can write this form code anywhere you like,
<%= form_for(:user, :url => session_path(:user)) do |f| %>
<%= f.text_field :email %>
<%= f.password_field :password %>
<%= f.check_box :remember_me %>
<%= f.label :remember_me %>
<%= f.submit 'Sign in' %>
<%= link_to "Forgot your password?", new_password_path(:user) %>
<% end %>

Adding to #maximus answer, you could use the Pundit gem to define your logged in :user action through a UserPolicy controller. Within this controller, you should be able to define which direction a passed in User navigates to defined by their logged in role.
https://github.com/elabs/pundit

Related

Adding role selection to devise sign up form, when role name needs to match role_id (using devise)

I'm new to rails (using rails 6), and I have a question that I didn't see answered on the site for my specific use case.
The basic scenario is the same as in many question - adding role based authentication to a rails application when using devise.
The way I implemented this was to create a roles table, and having a one to many relations between it and the users table.
I need only two users: guest and admin, and they have 1, 2 ids, respectively.
I inserted the values into the roles table manually, using the rails console.
Now, I'm having trouble with the sign up form, which is the default one devise gem created:
<h2>Sign up</h2>
<%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
<%= render "devise/shared/error_messages", resource: resource %>
<div class="field">
<%= f.label :email %><br />
<%= f.email_field :email, autofocus: true, autocomplete: "email" %>
</div>
<div class="field">
<%= f.label :password %>
<% if #minimum_password_length %>
<em>(<%= #minimum_password_length %> characters minimum)</em>
<% end %><br />
<%= f.password_field :password, autocomplete: "new-password" %>
</div>
<div class="field">
<%= f.label :password_confirmation %><br />
<%= f.password_field :password_confirmation, autocomplete: "new-password" %>
</div>
<div class="actions">
<%= f.submit "Sign up" %>
</div>
<% end %>
<%= render "devise/shared/links" %>
What I want to add is a select field that will show "admin" and "guest", and when the user submit the form, the right role_id will be added to the user record in the user table, but I don't get how to inform the devise view on the role table existence and how to match the string value of the role with the role id the I want the user to accualy have.
So my questions are:
How to add the roles to a select field in the devise sign up form?
How to handle after the user selects one of the roles string names, and match it with role id that will be added to the user when the form is processed?
I saw the following questions and many like them, but I didn't saw any of them dealing with matching string with id before the form is submitted:
Devise Add Admin Role
how to automatically add a role to a user when signing up using devise
Adding Fields To Devise Sign Up Using Rails 4
Multiple models associated with devise user - fill in one model based on selection of role
If there is a question answering my issue, I'm sorry and I would be glad to get the link and close this question.
If there is any more info you need, please let me know.
Thanks!
Using an enum to differentiate admins from guests seems like the best option and will prevent you from having to complicate your model/form with unnecessary things like nested attributes.
Here's an example...
Create a role column on the users table.
$ rails g migration add_role_to_users role:integer
In your migration file, make sure to set the default to 0.
add_column :users, :role, :integer, default: 0
Then, after migrating the db, in your User model add the enum...
class User < ApplicationRecord
enum role: { guest: 0, admin: 1 }
...
end
Adding another role is as simple as adding a key/value to the role enum.
Then, in your devise form, you can add something like...
<%= f.label :role %>
<%= f.select :role, User.roles.keys %>
You will also need to make sure that you are adding role as a permitted param...you seem to be adding it as a field in signup, so in ApplicationController...
class ApplicationController < ActionController::Base
before_action :configure_permitted_parameters, if: :devise_controller?
private
def configure_permitted_parameters
devise_parameter_sanitizer.permit(:sign_up, keys: [:role])
end
end
If you go with this you will also have to remove your roles table and associations.

Form for each user

I have an ordinary form to create a Package object at /packages/new:
<%= form_for #package do |f| %>
<%= f.text_field :name %>
<%= f.text_field :address %>
...
<% end %>
The package model belongs_to :partner.
I am looking for a way to associate a new package form to a specific partner, preferably without any input from the user filling it in.
For example, if partner A sends a link to the form, I want the form to include partner_id:A.id.
How can I connect forms to partners?
You can send partner_id param with the link which your partner will send.
Something like
http://website.com/packages/new?partner_id=3
And use the param as hidden_field in the form
<%= form_for #package do |f| %>
<%= f.text_field :name %>
<%= f.text_field :address %>
<%= f.hidden_field :partner_id, value: params[:partner_id] %>
...
<% end %>
Alternatively you can also make use of Nested Resources
you can have, hidden field which passes partner_id to controller
http://apidock.com/rails/ActionView/Helpers/FormHelper/hidden_field
If the partner needs to be logged in, in order to create a package, you could simply link the package to the partner in the controller right before saving it.
As mentioned before, use params. And don't forget to allow the required params in the controller if necessary (via link, scroll down a little). documentation: params
Check out what e.g. .build() does for you. more about relations and how to set them up correctly

Devise_invitable app/views/devise/invitation.html.erb 'registration' fields RoR

I'm sure there are people out there who have done this. I started down it, but I made my app crash a few times, so I figured I'd just ask out there rather than continue driving my webapp in to oblivion. I'm using devise_invitable gem. It sends a link to invited users, they click the link, and they're directed to this view # app/views/devise/invitation.html.erb:
<h2><%= t 'devise.invitations.edit.header' %></h2>
<%= simple_form_for resource, as: resource_name, url: invitation_path(resource_name), html: { method: :put } do |f| %>
<%= devise_error_messages! %>
<%= f.hidden_field :invitation_token %>
<%= f.input :password %>
<%= f.input :password_confirmation %>
<%= f.button :submit, t("devise.invitations.edit.submit_button") %>
<% end %>
I want to add some fields, for example
<%= f.input :firstname %>
When I do that, though, it does appear in the view, though it's not saving to the User model. So, I figured I needed to modify the controller. That's where I get confused, I think because I'm trying to flop back and forth between the devise and devise_invitable readme's. I'm using devise 3.5.6 and devise_invitable 1.5.5. I tried adding the above input to the form, and changing the applicaiton controller to include
before_action :configure_permitted_parameters, if: :devise_controller?
protected
def configure_permitted_parameters
devise_parameter_sanitizer.for(:sign_up) << :firstname
end
but that still doesn't save to my user model. Anyone have any advice?
You are permitting params that pass on to devise RegistrationsController create action. You can see it in definition, below params sanitizer is for :sign_up
devise_parameter_sanitizer.for(:sign_up) << :firstname
In your case it must be :accept_invitation, Since you are using devise_invitable and form submit url is invitation_path which will submit to Devise::InvitationsController#update
devise_parameter_sanitizer.for(:accept_invitation) do |u|
u.permit(:firstname)
end
More details here

Rails Advanced Sign Up Implementation How To?

I am a three week old Rails newbie and I have something that I want to implement but have no idea how to go about it. I'm making an app:
I want a user to enter some sign-up info on the new users view page, then, when they click submit, instead of the user being created and saved in the database right away, instead I want them to be taken to a second webpage where they will be asked for some final verification before they can create their account. Then when they click 'verify' and the verification passes, the account is finally created and saved to the database.
This is hard for me because I only know how to make basic forms, where you enter info, hit submit, and you have a new entry in the database. I don't know how to defer the "user creation" for another webpage, but a friend has mentioned something about http requests, but I still don't know anything. I'll post some of my code so far:
My users_controller new definition:
def new
#user = User.new
#user.websites.build
end
My users_controller create method:
def create
#user = User.new(params[:user])
if #user.save
sign_in #user
flash[:success] = "Welcome!"
redirect_to #user
else
render 'new'
end
end
My users/new.html.erb sign up form:
<% provide(:title, 'Sign up') %>
<h1>Sign up</h1>
<%= render 'shared/error_messages' %>
<%= form_for(#user) do |f| %>
<%= f.label :email %>
<%= f.text_field :email %><br/>
<%= f.label :password %>
<%= f.password_field :password %><br/>
<%= f.label :password_confirmation, 'Confirm Password' %>
<%= f.password_field :password_confirmation %><br/>
<%= f.fields_for :websites do |builder| %>
<%= builder.label :url, 'Website URL' %>
<%= builder.text_field :url %>
<% end %>
<%= f.submit "Sign up", :id => 'submit' %>
<% end %>
I've never asked a question that's just asking for advice like this before, so I'm hoping this is the right place to ask. Any and all help is appreciated.
I think you are trying to create a "multistep" form...
There is a very good railscast about it: http://railscasts.com/episodes/217-multistep-forms?view=asciicast
However, you might face some validation problems, as you need to validate each step individually. So, take a look also here: http://guides.rubyonrails.org/active_record_validations_callbacks.html#conditional-validation
I hope it helps...
You'd better use Devise for user management.
https://github.com/plataformatec/devise
Here's a railscast tutorial.
railscasts.com/episodes/209-introducing-devise
If you're doing multistep, you can always pass information from one page to another. You can store it in a session array, repost it to the other page, or even make a cookie.
Remember to also do every step of this under https, because account creation requires sensitive info.
If Rails is your first I would actually recommend something lower level like PHP so you can understand how everything works before you start doing stuff with a high level framework like rails. MVC is usually not the best first step in web development, even though it is powerful and easier than doing everything from scratch.

Rails Authentication - Query on Customizing Devise Views

I am a Devise fan and have used Devise for many of my Ruby on Rails applications .
I usually follow the below RailCasts for installing and customizing Devise .
http://railscasts.com/episodes/209-introducing-devise
http://railscasts.com/episodes/210-customizing-devise
The video tells us that if we want to customize the views , we need to run the below command -
rails generate devise:views
This creates the views related to authentication in the apps/views/devise folder . We can customize the look and feel .
My questions is - How do we allow multiple views for signing in ? For eg - I want to allow the user to be able to Login from the Root page itself by clicking a Login button on a navbar which leads to a modal box ( overlay / popup ) which allows the user to enter his login credentials , instead of having to visit the predefined pages generated by Devise.
You can have a look at the below link to see the project I am working on now .
http://squilio.heroku.com/ .
Try clicking on the Login button. I would like to have my signin form here .
the railscast episode you saw is too old ( Apr 12, 2010), latest Devise version is 2.0. so I think some of the features is deprecated.
For your question: If you want to have 2 different pages (e.g. 1 is a dialog/pop up by ajax, another is regular erb page), you have to implement 2 different actions, or at least you have to implement 2 different strategies for them. e.g.
# will render app/views/.../regular_login.html.erb
def regular_login
end
# render a dialog box only.
def dialog_login
render :layout => false
end
I found an elegant solution from this article ..
https://github.com/plataformatec/devise/wiki/How-To:-Display-a-custom-sign_in-form-anywhere-in-your-app
It shows how we can achieve this by using form_for and posting to user_session_path
<%= form_for("user", :url => user_session_path) do |f| %>
<%= f.text_field :email %>
<%= f.password_field :password %>
<%= f.check_box :remember_me %>
<%= f.label :remember_me %>
<%= f.submit 'Sign in' %>
<%= link_to "Forgot your password?", new_password_path('user') %>
<% end %>

Resources