I was wondering how you flip the flow of a website.
Example:
Normally a visitor needs an user login to write a summary.
When the visitor come to the page and click create summary, are the visitor normally redirect to sign up page.
How to flip the flow. So you can write a summary without login. And when you click next, you need to login or create a user. User decides to create an user and the summary are created with an association or the user have a login and it also creates and association.
How to create this with devise?
I suggest the following approach:
After creating the summary, store its ID in a session variable. Then, when the user signs in, if any summary IDs are found in their session, associate them with the signed in user
For example:
class SummaryController < ApplicationController
def create
#summary = Summary.create!(params[:summary])
unless user_signed_in?
session[:anon_summary_ids] ||= []
session[:anon_summary_ids] << #summary.id
redirect_to(user_sign_in_path, :notice => "Thanks for the summary! Please sign in...")
end
end
end
class ApplicationController
before_filter :associate_summaries, :if => [:user_signed_in?, :anon_summaries?]
private
def anon_summaries?
session[:anon_summary_ids].try(:any?)
end
def associate_summaries
Summary.where(:id => session[:anon_summary_ids], :user_id => nil).update_all(:user_id => current_user.id)
session[:anon_summary_ids] = nil
end
end
Related
I am using Devise for my users in my rails app.
In my user model i have column randomID, this column is uquinesse for each user
and i generate it when the user sign-up
In the user model i have also addBY, i use that because the user need the randomID of another user for sign-up
So i will have tree relation between all users
all works fine, but my brobleme now is if the user enter the wrong randomID,
i must check if exist in my DATABASE or not
if the value exist i let the user sign-up
else i display a message error
any ideas
Simply call a function to check for a value existence before your create action.
in your users_controller.rb:
class UsersController < ApplicationController
before_action :check_for_token, only: :create
...
private
def check_for_token
redirect_to root_path unless User.exists?(:randomID => params[:addBY])
end
end
I am using devise and want to redirect users to a confirmation page upon signup, this is what I am doing right now:
users/registrations_controller.html.erb
class Users::RegistrationsController < Devise::RegistrationsController
def confirm_email
end
private
def after_inactive_sign_up_path_for(resource)
users_confirmyouremail_path
end
end
config/routes.rb
devise_scope :user do
get 'users/confirmyouremail' => 'users/registrations#confirm_email'
end
I have no problem with redirecting the page after signup. However, I think it is quite weird that anyone can visit the page with url like `host.com/confirmyouremail' and see the confirmation page. Are there any ways I can write a route that will use random code that is allow only for one time visit? Thanks in advance.
Maybe something like this:
before_action :authenticate_user!
def confirm_mail
redirect_to root_path if current_user.confirmed
...
end
You are storing in the database if the user has already confirmed his account. If his account is confirmed then he won't be able to access this page. You can redirect to whatever page you want. A user without any account won't be able to access this page because of the before action
In case the user is not logged in when he accesses this confirm_mail page you have different possibilities. You could use a session or a cookie:
# after sign up:
session[:confirm] = true
# alternatively a cookie
cookies[:confirm] = true
Then in the confirm mail action:
def confirm_mail
if session[:confirm].blank? # or cookies[:confirm].blank?
redirect_to root_path
end
# otherwise delete the field from the session
session.delete(:confirm)
# alternatively the cookie
cookies.delete(:confirm)
end
Another way would be by using a Token. You create a new model like ConfirmMailToken. Then on sign up you create a new token and redirect the user to the confirm page with the token as a URL param. Then in the confirm_mail action you check if a token is available and delete it if it is. This way you ensure that the page is only shown after redirect.
I have a blog project in Ruby on Rails.
I want to allow user to leave message without sign in BUT after creating the message I want him to sign in or signup.
The idea is like following, user clicks on "Create Message" then he fill the form of the message, then he clicks on the "Save Message" and if user is not signed in then he redirected to signin/signup form, after signin the system should identify that we have a message for this user, save the message with correct user.id.
I know it's pretty standard technique, if you know some good pattern to do so I would be glad.
In your create action, you should distinguish between 1) user is known and 2) user is not known yet. In the second case, you should store the id of the created message in your session, then redirect to the login/signup page:
session[:message] = #message.id
redirect_to new_user_session_path
Next step is to interfere with both the create action of the registrations controller (user signed up after your redirect), and with the create action of the sessions controller (user signed in after your redirect)
In both controllers, you can add something like
after_filter :assign_pending_message, only: :create
with
def assign_pending_information_request
# User succesfully created?
return unless #user.id
if session[:message]
if Message.exists?(id: session[:message])
message = Message.find(session[:message])
message.user = #user
message.save!
end
session.delete(:message)
end
end
If you need more details, just let me know!
Devise doesn't allow POST parameters to be sent on authentication, so you need to send message parameters via GET, So here user will be redirected to the login page on clicking save message because of the applied filter, Here is a demo
class MessagesController < ApplicationController
before_filter :authenticate_user!, :except => [:new]
def new
#message = Message.new
end
def get_message
#message = Message.new(params[:message])
#message.save
end
end
In the form you explicitly need to mention the URL of get_message action otherwise it will take create by default, create is a post method so it is of no use to us to implement this.
in your routes.rb
resources :messages do
get :get_message, :on => :collection
end
In my application, users have the ability to invite others to be 'Contributors' on their account. These contributors once they have logged in, are redirected to a dashboard that shows each account they can log in as.
The below is a controller that is used to allow Contributors and School Admins to log into Athlete accounts. When the original user logs into an account the app checks for the session[:original_user_id] variable to display a message banner across the top of the screen with a link so that the admin can log back into their account - This is what I am having issues with trying to figure out how to log the original user back in.
SignInAsController:
class SignInAsController < ApplicationController
before_filter :authenticate_user!
include SchoolAdmin::Athletes
def create
session[:original_user_id] = if (current_user.school_admin? || current_user.athlete_contributor?)
current_user.id
else
nil
end
user = User.find(params[:id])
if current_user.can_manage?(user)
sign_out(User.find(current_user.id))
handle_request(athlete)
redirect_to user_root_path
else
redirect_to :back, notice: "You do not have access to that account"
end
end
private
def handle_request(athlete)
sign_in(:user, athlete, { bypass: true })
end
end
UserModel can_manage? method:
class User < ActiveRecord::Base
#other methods
def can_manage?(user)
if athlete_contributor?
managed_athletes.include?(user)
elsif school_admin?
active_subscription.athletes.include?(user)
end
false
end
end
This is what worked for me. It allows an Admin user to login as another User.
class AdminController < ApplicationController
before_filter :authenticate_user!
def become
return unless current_user.is_an_admin?
sign_in(:user, User.find(params[:id]))
redirect_to root_path
end
end
You can also do sign_in(:user, User.find(params[:id]), { :bypass => true }) if you don't want to update last_sign_in_at and current_sign_in when admin becomes the user.
Devise has sessions & registrations controllers, which allow you to create & override various methods for devise
Sessions
Considering the logging-in process is about creating a session, I'd look at implementing my own method to destroy the current session & create a new one with the admin stats, like this:
#config/routes.rb
devise_for :users, :controllers => { :sessions => "sessions" }
devise_scope :user do
post "admin", :to => "devise/sessions#admin_switch"
end
#app/controllers/sessions_controller.rb
class SessionsController < Devise::SessionsController
#Switch to "admin" mode
def admin_switch
sign_out #method to destroy current user's session
sign_in #create new session -- needs more work
end
end
This is meant to demonstrate how you'd achieve your goal. If you want me to add some more code, I certainly will amend my post! Specifically, the sign_in command won't work (needs specific data passed to it, and uses Warden)
You can use Switch User gem which can work with:
:devise, :authlogic, :clearance, :restful_authentication or :sorcery
https://github.com/code-and-effect/effective_website/blob/develop/app/controllers/users/impersonations_controller.rb
this functionality “impersonating another user”. an admin can go onto Admin::Users#index -> Find a user, and impersonate them. That is, sign into their account.need a button that posts to this action
https://github.com/code-and-effect/effective_website/blob/develop/app/controllers/admin/users_controller.rb#L11
def impersonate
#user = User.find(params[:id])
authorize! :impersonate, #user
# Impersonate
session[:impersonation_user_id] = current_user.id
expire_data_after_sign_in!
warden.session_serializer.store(#user, Devise::Mapping.find_scope!(#user))
redirect_to(root_path)
end
before_action :authenticate_user!
skip_authorization_check only: [:destroy]
def destroy
#user = User.find(session[:impersonation_user_id])
# Reset impersonation
session[:impersonation_user_id] = nil
expire_data_after_sign_in!
warden.session_serializer.store(#user, Devise::Mapping.find_scope!(#user))
redirect_to(admin_users_path)
end
end
Which uses devise, to set the session[:impersonation_user_id] so later you know you are impersonating another use, and then this warden.session_serializer.store which signs you in as a new user.
If you close the tab. Reopen the tab. You will still be impersonating that user.
Put a partial in the site (haml) to display an alert to the user on every page when they’re impersonating
.bg-warning.d-print-none.text-center
You are logged in as <strong>#{current_user}</strong>.
= link_to 'click here', impersonate_path, 'data-method': :delete
to return to your original account.
https://github.com/code-and-effect/effective_website/blob/develop/app/views/layouts/_impersonate.html.haml
With the Confirmable module enabled, Devise will not allow an unconfirmed user to sign in after a predefined period of time has elapsed. Instead the user is redirected back to the sign in page with the flash message "You have to confirm your account before continuing".
This is an undesirable interaction model, as a flash notice does not provide adequate space to properly explain to the user why access has been denied, what "confirm your account" means, provide a link to resend the confirmation, and instructions on how to check your spam folder and so on.
Is there a way I can change this behaviour to redirect to a specific URL instead?
Sorry at first I thought you meant after Sign Up not Sign In. So the down below works for how to direct users after Sign Up and what you need to do for Sign In is to create a custom Devise::FailureApp
See the wiki page: https://github.com/plataformatec/devise/wiki/How-To:-Redirect-to-a-specific-page-when-the-user-can-not-be-authenticated
Then within your custom FailureApp overwrite redirect_url method from https://github.com/plataformatec/devise/blob/master/lib/devise/failure_app.rb:
def redirect_url
if warden_message == :unconfirmed
custom_redirect_path
else
super
end
end
For custom redirect after Sign Up:
There is a controller method after_inactive_sign_up_path_for within the RegistrationsController that you can overwrite to accomplish this.
First in your Routes you will need to specify to use your custom controller:
config/routes.rb:
devise_for :users, :controllers => { :registrations => "users/registrations" }
Second you create your custom controller that inherits from the normal controller in order to overwrite the method:
app/controllers/users/registrations_controller.rb
class Users::RegistrationsController < Devise::RegistrationsController
protected
def after_inactive_sign_up_path_for(resource)
signed_up_path
end
end
In this case for my App my Devise model is User so you may want to change that namespace if your model is named differently. I wanted my users to be redirected to the signed_up_path, but you can change that to your desired path.
I just did this, but took a different approach.
in app/controllers/sessions_controller.rb:
class SessionsController < Devise::SessionsController
before_filter :check_user_confirmation, only: :create
#
# other code here not relevant to the example
#
private
def check_user_confirmation
user = User.find_by_email(params[:email])
redirect_to new_confirmation_path(:user) unless user && user.confirmed?
end
end
This worked for me and seemed minimally invasive. In my app new sessions always have to go through sessions#create and users always sign in with their email address, so this may be a simpler case than yours.
You can of course redirect_to any location you desire in the check_user_confirmation method. new_confirmation_path was the logical choice for me because it provides users with the resources to get confirmed.
This is my solution you need to add :unconfirmed message on devise locales below the sessions.
in app/controllers/sessions_controller.rb
def check_user_confirmation
user = User.where(email: params[:user][:email]).take
unless user && user.confirmed?
set_flash_message! :alert, :unconfirmed
expire_data_after_sign_in!
respond_with user, location: after_inactive_sign_up_path_for(user)
end
end
protected
def after_inactive_sign_up_path_for(resource)
new_user_session_path
end