I've got a pretty standard User model with Devise. Administrators are controlled with an :admin boolean on the model, and users who aren't administrators cannot manage themselves (i.e., only administrators can make changes to users).
What I'd like is to permit users to reset their password if they forget it. They would enter their email address then be emailed a token which would grant them access to change it. I've started a solution but I really don't know how to proceed.
user.rb
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable, :recoverable, :rememberable, :trackable, :validatable
attr_accessor :current_password
attr_accessible :name, :password, :password_confirmation, :current_password, :email, :remember_me, :ftp, :colour1, :colour2, :logo, :logo2, :address, :url, :disclosure, :general_advice, :facebook, :twitter, :brand_id, :analytics
attr_protected :admin
validates_uniqueness_of :email, :ftp
belongs_to :brand
has_many :docs
has_many :orders, :through => :docs
has_attached_file :logo
has_attached_file :logo2
end
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new #guesting
if user.admin?
can :manage, :all
else
can :manage, :recoverable
end
end
end
And here's the method:
def reset
#idiot = User.where(:email => params[:email]).first
unless #idiot.nil?
Notifier.send_reset_notice(#idiot).deliver
#idiot.send_reset_password_instructions
redirect_to new_user_session_url
else
redirect_to new_user_session_url, :flash => { :error => "That email address matches no user." }
end
end
The user receives the Devise email but clicking on the link takes the user to the application root and not to a password reset form. I'm not sure how to proceed from here. Any thoughts? Cheers!
UPDATE
Relevant routes:
devise_for :users, :path => "d"
devise_scope :user do
get '/sign_in' => 'devise/sessions#new'
get '/sign_out' => 'devise/sessions#destroy'
end
First, create controller for handle Devise::PasswordsController
class PasswordusersController < Devise::PasswordsController
prepend_before_filter :require_no_authentication
append_before_filter :assert_reset_token_passed, :only => :edit
def new
super
end
def create
self.resource = resource_class.send_reset_password_instructions(resource_params)
if successfully_sent?(resource)
redirect_to root_path, :notice => "Instruction has been send to your email"
else
respond_with(resource)
end
end
def edit
super
end
def update
self.resource = resource_class.reset_password_by_token(resource_params)
if resource.errors.empty?
resource.unlock_access! if unlockable?(resource)
flash_message = resource.active_for_authentication? ? :updated : :updated_not_active
set_flash_message(:notice, flash_message) if is_navigational_format?
sign_in(resource_name, resource)
redirect_to login_path, :notice => "Password has been change"
else
respond_with resource
end
end
protected
def after_sending_reset_password_instructions_path_for(resource_name)
root_path
end
def assert_reset_token_passed
super
end
def unlockable?(resource)
super
end
end
Than, run generate view for devise
copy new.html.erb and edit.html.erb from views/devise/passwords to views/passwordusers
on routes.rb you can config such as :
devise_scope :user do
get '/reset_password' => "passowrdusers#new", :as => :reset_password
get '/new_password' => "passwordusers#edit", :as => :new_password
end
edit link on devise/mailer/reset_password_instructions.html.erb
<p><%= link_to 'Change My Password', new_password_path(#resource, :reset_password_token => #resource.reset_password_token) %></p>
finally, on view form login add this
<%= link_to "Forgot Password?", reset_password_url(resource_name) %>
UPDATE
on routes.rb you can config such as :
devise_scope :user do
get '/reset_password' => "passowrdusers#new", :as => :reset_password
get '/new_password' => "passwordusers#edit", :as => :new_password
post '/send_email' => 'passwordusers#create', :as => :create_password
put '/change' => 'passwordusers#update', :as => :update_password
end
on views/passwordusers/new.html.erb
<%= form_for("user", :url => create_password_path(resource_name), :html => { :method => :post }) do |f| %>
on views/passwordusers/edit.html.erb
<%= form_for("user", :url => update_password_path(resource_name), :html => { :method => :put }) do |f| %>
Related
I am trying (again) to set up authentications with Rails 4, devise and omniauth.
I tried to follow the example in this post: Rails 4, Devise, Omniauth (with multiple providers)
I have these gems installed:
gem 'devise'
gem 'omniauth-google-oauth2'
gem 'omniauth-facebook'
gem 'omniauth-twitter'
gem 'omniauth-linkedin-oauth2'
gem 'oauth2'
I have a user model, and an authentications model.
I have:
User.rb:
has_many :authentications
def disapprove
self.approved = false
end
def approve
self.approved = true
end
SOCIALS = {
facebook: 'Facebook',
google_oauth2: 'Google',
linkedin: 'Linkedin'
}
def self.from_omniauth(auth, current_user)
authentication = Authentication.where(:provider => auth.provider,
:uid => auth.uid.to_s,
:token => auth.credentials.token,
:secret => auth.credentials.secret).first_or_initialize
authentication.profile_page = auth.info.urls.first.last unless authentication.persisted?
if authentication.user.blank?
user = current_user.nil? ? User.where('email = ?', auth['info']['email']).first : current_user
if user.blank?
user = User.new
user.skip_confirmation!
user.password = Devise.friendly_token[0, 20]
user.fetch_details(auth)
user.save
end
authentication.user = user
authentication.save
end
authentication.user
end
def fetch_details(auth)
self.first_name = auth.info.first_name
self.last_name = auth.info.last_name
self.email = auth.info.email
self.image = URI.parse(auth.info.image)
end
Authentication.rb
belongs_to :user
Routes.rb
devise_for :users,
:controllers => {
:registrations => "users/registrations",
:omniauth_callbacks => 'users/omniauth_callbacks',
}
User/registrations_controller
class Users::RegistrationsController < Devise::RegistrationsController
#before_filter :check_permissions , :only => [ :new, :create, :cancel ]
#skip_before_filter :require_no_authentication
def check_permissions
authorize! :create, resource
end
def index
if params[:approved] == "false"
#users = User.find_all_by_approved(false)
else
#users = User.all
end
end
def create
#user = User.new(user_params) #(params[:user])
respond_to do |format|
if resource.save
# Tell the UserMailer to send a welcome email after save
# {#user.send_admin_mail
# #user.send_user_welcome_mail}
format.html { redirect_to(profile_path(#user.profile))}
#, notice: 'We have received your registration. We will be in touch shortly.') }
#format.json { render json: root_path, status: :created, location: #user }
else
#format.html { redirect_to(root_path, alert: 'Sorry! There was a problem with your registration. Please contact us to sort it out.') }
format.html { render action: 'new' }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
private
def user_params
params.require(:user).permit(:first_name, :last_name, :email, :password )
end
# protected
# def after_sign_up_path_for(resource)
# 'subscribers/new'
# end
end
User/Omniauth_callbacks controller
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
# try again following https://stackoverflow.com/questions/21249749/rails-4-devise-omniauth-with-multiple-providers
def all
user = User.from_omniauth(env['omniauth.auth'], current_user)
if user.persisted?
sign_in user
flash[:notice] = t('devise.omniauth_callbacks.success', :kind => User::SOCIALS[params[:action].to_sym])
if user.sign_in_count == 1
redirect_to profile_path(#user.profile)
else
redirect_to root_path
end
else
session['devise.user_attributes'] = user.attributes
redirect_to root_path
end
end
User::SOCIALS.each do |k, _|
alias_method k, :all
end
end
The devise/new registrations view says:
<%= simple_form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
<div class="row">
<div class="col-md-3 col-md-offset-3">
<div class="row">
<div class="col-md-12">
<%- if devise_mapping.omniauthable? %>
<div class="facebookauth">
<%= link_to "Join with Facebook", user_omniauth_authorize_path(:facebook) %>
</div>
<% end -%>
</div>
</div>
<div class="row">
<div class="col-md-12">
<%- if devise_mapping.omniauthable? %>
<div class="googleauth">
<%= link_to "Join with Google", user_omniauth_authorize_path(:google_oauth2) %>
</div>
<% end -%>
</div>
</div>
<div class="row">
<div class="col-md-12">
<%- if devise_mapping.omniauthable? %>
<div class="linkedinauth">
<%= link_to "Join with LinkedIn", user_omniauth_authorize_path(:linkedin) %>
</div>
<% end -%>
</div>
</div>
<div class="row">
<div class="col-md-12">
<%- if devise_mapping.omniauthable? %>
<div class="twitterauth">
<%= link_to "Join with Twitter", user_omniauth_authorize_path(:twitter) %>
</div>
<% end -%>
</div>
</div>
</div>
<div class="col-md-5">
<div class="emailform">
<div class="form-inputs", style="margin-left: 7%">
<%= devise_error_messages! %>
<%= f.input :first_name, :label_html => {:class => 'deviselabels'}, autofocus: true, required: false, :input_html => {:maxlength => 15, :size => 40, class: 'lineitemdevise'} %>
<%= f.input :last_name, :label_html => {:class => 'deviselabels'}, required: false, :input_html => {:maxlength => 15, :size => 40, class: 'lineitemdevise'} %>
<%= f.input :email, :label_html => {:class => 'deviselabels'}, required: false, autofocus: false, placeholder: "Please use your work or university address", :input_html => {:maxlength => 55, :size => 40, class: 'lineitemdevise'} %>
<%= f.input :password, :label_html => {:class => 'deviselabels'}, required: false, placeholder: "Minimum 8 characters", :input_html => {:maxlength => 15, :size => 40, class: 'lineitemdevise'} %>
</div>
<div class="form-actions">
<%= f.button :submit, "Join by email", :class => "dcpb" %>
</div>
<% end %>
</div>
</div>
I have another model called profile.rb.
profile belongs_to user
Problems:
None of this works. When I click on each of the social media login links, the page just jumps to the sign up by email form.
The heroku logs error message says:
(facebook) Authentication failure! invalid_credentials: OAuth2::Error, :
2015-11-03T07:05:48.237549+00:00 app[web.1]: {"error":{"message":"Error validating verification code. Please make sure your redirect_uri is identical to the one you used in the OAuth dialog request","type":"OAuthException","code":100,"fbtrace_id":"HD3mnzmSTEw"}}
When I complete the sign up by email form with an email and password, the user name is recognised (in that the navbar says Hello , but when I go into the rails console, the user is not listed.
Also, when I click on the user name, I get an error which says that profile does not exist. The heroku logs say:
(Couldn't find Profile with 'id'=3)
Is there another step required to make the social media registrations work to create a new user?
MY NEXT ATTEMPT:
I've changed all of the above and tried again, following the approach in the Railscasts videos.
I now use a user model and an authentications model.
In the user.rb, I have:
has_many :authentications, :dependent => :delete_all
def apply_omniauth(omniauth)
authentications.build(:provider => omniauth['provider'], :uid => omniauth['uid'], :token => auth['credentials']['token'])
end
authentication.rb
belongs_to :user
def self.from_omniauth(auth)
where(auth.slice(:provider, :uid)).first_or_create do | user |
authentication.provider = auth.provider
authentication.uid = auth.uid
authentication.user.first_name = auth.first_name
authentication.user.last_name = auth.last_name
authentication.user.image = auth.info.image
end
end
Authentications_controller:
class AuthenticationsController < ApplicationController
before_action :set_authentication, only: [:destroy]
def index
#authentications = current_user.authentications if current_user
end
def create
omniauth = request.env["omniauth.auth"]
authentication = Authentication.find_by_provider_and_uid(omniauth['provider'], omniauth['uid'])
if authentication
sign_in_and_redirect_user(:user, authentication.user.profile)
elsif current_user
current_user.authentications.create!(:provider => omniauth['provider'], :uid => omniauth['uid'])
redirect_to user.profile_url
else
user = User.new
user.omniauth(omniauth)
if user.save!
sign_in_and_redirect_user(:user, user.profile)
else
session[:omniauth] = omniauth.except('extra')
redirect_to new_user_registration_url
end
end
end
def destroy
#authentication.destroy
respond_to do |format|
format.html { redirect_to authentications_url }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_authentication
#authentication = current_user.authentications.find(params[:id])
end
end
In the routes.rb, I have:
devise_for :users,
:controllers => {
:registrations => "users/registrations",
}
patch '/auth/:provider/callback' => 'authentications#create'
Omniauth.rb
require 'omniauth-facebook'
require 'omniauth-google-oauth2'
OmniAuth.config.logger = Rails.logger
Rails.application.config.middleware.use OmniAuth::Builder do
provider :facebook, ENV['FACEBOOK_ID'], ENV['FACEBOOK_KEY'],
:scope => 'public_profile', info_fields: 'id,first_name,last_name,link,email',
:display => 'popup',
:client_options => {:ssl => {:ca_file => '/usr/lib/ssl/certs/ca-certificates.crt'}}
Then when I try this, I get this error:
(facebook) Authentication failure! invalid_credentials: OAuth2::Error, :
2015-11-05T06:4839+00:00 app[web.1]: {"error":{"message":"Error validating verification code. Please make sure your redirect_uri is identical to the one you used in the OAuth dialog request","type":"OAuthException","code":100,"fbtrace_id":"CrvXN22Z"}}
I find the next part of the error message odd because it refers to the callbacks controller which I no longer use (the whole thing is commented out and there is no route for it).
Authentication failure! invalid_credentials: OAuth2::Error, :
2015-11-05T08:24:16.010951+00:00 app[web.1]: Processing by Devise::OmniauthCallbacksController#failure as HTML
2015-11-05T08:24:16.012648+00:00 app[web.1]: Redirected to http://www.dder.com/users/sign_in
A FURTHER ATTEMPT
I have been trying to set up devise with omniauth for more than 1.5 years now. This is my latest attempt (following the Sitepoint tutorial at sitepoint.com/rails-authentication-oauth-2-0-omniauth). I've tried to use this tutorial before and not had any success, so I've made some tweaks to try and adapt it to some aspects of other tutorials on this topic.
I now have:
user.rb
has_many :authentications, :dependent => :delete_all
def apply_omniauth(omniauth)
authentications.build(:provider => omniauth['provider'], :uid => omniauth['uid'], :token => auth['credentials']['token'])
end
authentication.rb
belongs_to :user
def self.from_omniauth(auth)
where(auth.slice(:provider, :uid)).first_or_create do | user |
authentication.provider = auth.provider
authentication.uid = auth.uid
authentication.user.first_name = auth.first_name
authentication.user.last_name = auth.last_name
authentication.user.image = auth.info.image
end
end
authentications controller
class AuthenticationsController < Devise::OmniauthCallbacksController
before_action :set_authentication, only: [:destroy]
def index
#authentications = current_user.authentications if current_user
end
def create
omniauth = request.env["omniauth.auth"]
authentication = Authentication.find_by_provider_and_uid(omniauth['provider'], omniauth['uid'])
if authentication
sign_in_and_redirect_user(:user, authentication.user.profile)
elsif current_user
current_user.authentications.create!(:provider => omniauth['provider'], :uid => omniauth['uid'])
redirect_to user.profile_url
else
user = User.new
user.omniauth(omniauth)
if user.save!
sign_in_and_redirect_user(:user, user.profile)
else
session[:omniauth] = omniauth.except('extra')
redirect_to new_user_registration_url
end
end
end
def destroy
#authentication.destroy
respond_to do |format|
format.html { redirect_to authentications_url }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_authentication
#authentication = current_user.authentications.find(params[:id])
end
end
registrations controller
class Users::RegistrationsController < Devise::RegistrationsController
#before_filter :check_permissions , :only => [ :new, :create, :cancel ]
#skip_before_filter :require_no_authentication
# before_action :configure_permitted_parameters, if: :devise_controller? # Suggestion from Sitepoint tutorial - not currently implemented because not sure about the difference between this and set params.
def check_permissions
authorize! :create, resource
end
def index
if params[:approved] == "false"
#users = User.find_all_by_approved(false)
else
#users = User.all
end
end
def create
super
session[:omniauth] = nil unless #user.new_record?
end
# THIS IS A SUGGESTION FROM SITEPOINT TUTORIAL
# protected
# def configure_permitted_parameters
# devise_parameter_sanitizer.for(:sign_up) << [:first_name, :last_name]
# end
private
def user_params
params.require(:user).permit(:first_name, :last_name, :email, :password )
end
def build_resource(*args)
super
if session[:omniauth]
#user.apply_omniauth(session[:omniauth])
#user.valid?
end
end
end
routes
devise_for :users,
:controllers => {
:registrations => "users/registrations",
:omniauth_callbacks => "authentications"
# :omniauth_callbacks => 'users/omniauth_callbacks',
}
get '/auth/:provider/callback' => 'authentications#create'
I can check these routes with:
rake routes | grep auth
user_omniauth_authorize GET|POST /users/auth/:provider(.:format) authentications#passthru {:provider=>/facebook|linkedin|twitter|google_oauth2/}
user_omniauth_callback GET|POST /users/auth/:action/callback(.:format) authentications#:action
GET /auth/:provider/callback(.:format) authentications#create
new registration partial in the view
<%- if devise_mapping.omniauthable? %>
<div class="twitterauth">
<%= link_to "Join with Twitter", user_omniauth_authorize_path(:twitter) %>
</div>
<% end -%>
I'm really not sure where this path is coming from. Not sure why it's named as it is.
new session partial in the view
<%- if devise_mapping.omniauthable? %>
<div class="twitterauth">
<%= link_to "Login with Twitter", user_omniauth_authorize_path(:twitter) %>
</div>
<% end -%>
Current error:
AbstractController::ActionNotFound at /users/auth/twitter/callback
The action 'twitter' could not be found for AuthenticationsController
Devise comes with an out of the box solution for integration with omniuth. You can checkout these urls:
1. https://www.digitalocean.com/community/tutorials/how-to-configure-devise-and-omniauth-for-your-rails-application
This one shows integration with DigitalOcean but can be extended to others.
2. https://github.com/plataformatec/devise/wiki/OmniAuth%3A-Overview
This one is from devise wiki
Hope it helps
There is a gem called dom that was made exclusively to manage Devise with multiple providers. It makes things deadly simple!
Also, I think you should read these articles. I'm sure you can solve all your questions with them:
Devise OmniAuth: Overview
OmniAuth Managing multiple provaders
Im trying to build the functionality that gives users the ability to send each other messages. Ideally, a user can visit another user's profile and click on a link the redirects them to a form in which they will enter the message, and then click send and it will be delivered to the second user's inbox.
Separately, users should be able to visit an inbox page that displays all messages that have been sent to them.
I'm working on the first part now, and having trouble with the routes. Specifically, when I click on the send message link in a user's profile I'm redirected to the user's inbox page instead of being redirected to a form in which I can enter the context of my message and send. I'm not sure what I'm doing wrong here. Can anyone suggest how I can solve this routing issue?
routes.rb
devise_for :users, :controllers => { :registrations => "registrations" }
devise_scope :user do
get 'register', to: 'devise/registrations#new'
get 'login', to: 'devise/sessions#new', as: :login
get 'logout', to: 'devise/sessions#destroy', as: :logout
end
resources :users do
member do
get 'edit_profile'
get 'create_message'
end
end
resources :messages
root to: "home#index"
match '/about', to: 'static_pages#about', via: 'get'
match '/contact', to: 'static_pages#contact', via: 'get'
match '/help', to: 'static_pages#help', via: 'get'
match '/legal', to: 'static_pages#legal', via: 'get'
end
users_controller.rb
class UsersController < ApplicationController
before_filter :authenticate_user!
def index
#users = User.all
end
def show
#user = User.find(params[:id])
end
def new
end
def create
end
def edit
end
def update
#user = User.find(params[:id])
#user.update!(user_params)
redirect_to #user
end
def destroy
end
def edit_profile
#user = User.find(params[:id])
end
def create_message
end
def user_params
params.require(:user).permit(:first_name, :last_name, :email, :password, :password_confirmation, :current_industry, :years_in_current_industry, :hobbies)
end
def sender
#user = User.find(params[:id])
end
def recipient
#user = User.find(params[:id])
end
end
messages_controller.rb
class MessagesController < ApplicationController
def index
end
def new
end
def create
end
def destroy
end
end
user.rb
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
attr_accessible :first_name, :last_name, :email, :password, :password_confirmation, :remember_me, :current_industry, :years_in_current_industry, :hobbies
#validates :first_name, presence: true, length: { maximum: 50 }
#validates :last_name, presence: true, length: { maximum: 50 }
end
messages.rb
class Messages < ActiveRecord::Base
belongs_to :sender
belongs_to :recipient
default_scope -> [ order('created_at DESC') ]
validates :sender_id, presence: true
validates :recipient_id, presence: true
end
index.html.erb
<h1>Inbox</h1>
creates_messages.html.erb
<h2>Create Message</h2>
<%= form_for #user do |f| %>
<%= f.label :content %><br />
<%= f.text_area :first_name, autofocus: true %>
<div><%= f.submit "Send" %></div>
<% end %>
Here's what I'd do:
#config/routes.rb
devise_for :users, path: "", :controllers => { :registrations => "registrations" }, path_names: {sign_up: "register", sign_in: "login", sign_out: "logout"}
resources :users do
resources :messages, only: [:new, :create]
end
resources :messages, only: [:index, :show, :destroy] #domain.com/messages -> inbox
#app/controllers/messages/new.html.erb
class MessagesController < ApplicationController
before_action :set_recipient
def new
#message = current_user.messages.new recipient_id: params[:user_id]
end
def create
#message = current_user.messages.new message_params
#message.sender_id = current_user.id
#message.recipient_id = #recipient.id
#message.save
end
private
def message_params
params.require(:message).permit(:title, :body, :sender_id, :recipient_id)
end
def set_recipient
#recipient = User.find params[:user_id] if params[:user_id].present?
end
end
#app/views/messages/new.html.erb
<%= form_for [#recipient, #message] do |f| %>
<%= f.text_field :title %>
<%= f.text_field :body %>
<%= f.submit %>
<% end %>
Routes
The above code will give you the ability to use the following link:
#app/views/users/show.html.erb
<%= link_to "Send Message", user_message_path(#user) %>
You must remember that since Rails is object-orientated, everything you do (routes included) should revolve around objects. This is why you see the resources directive in your routing patterns -- it gives you the ability to define a series of CRUD based routes with various objects at their core.
I believe your problem is that you are not using the object orientated nature of Rails to proper extent. If you apply the routes as my example above, backed up with the relative methods, should give you the ability to create a very compelling flow.
I am newbie trying to add facebook login to my rails app using omniauth & 2 and omniauth-facebook. I followed the instructions on https://github.com/plataformatec/devise/wiki/OmniAuth:-Overview.
But it is showing me the below error now, I have searched around the net and tried many different ways without much success, I have no idea what I am doing wrong, so can I someone help me with this? much appreciated.
Running on
rails (3.2.12)
Using oauth2 (0.8.1)
Using omniauth (1.1.4)
Using omniauth-oauth2 (1.1.1)
Using omniauth-facebook (1.4.1)
Error:
ActiveRecord::RecordNotFound in UsersController#show
Couldn't find User with id=sign_up
My code
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :token_authenticatable, :confirmable,
# :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable, :recoverable, :rememberable, :trackable, :validatable, :omniauthable, :omniauth_providers => [:facebook]
# Setup accessible (or protected) attributes for your model
attr_accessible :email, :password, :password_confirmation, :remember_me, :name, :provider, :uid, :name
attr_accessible :title, :date_of_birth, :firstName, :lastName, :mailId, :phone, :provider, :uid
protected
def self.find_for_facebook_oauth(auth, signed_in_resource=nil)
user = User.find(:provider => auth.provider, :uid => auth.uid).first
unless user
user = User.create(name:auth.extra.raw_info.name,
provider:auth.provider,
uid:auth.uid,
email:auth.info.email,
password:Devise.friendly_token[0,20]
)
end
user
end
def self.new_with_session(params, session)
super.tap do |user|
if data = session["devise.facebook_data"] && session["devise.facebook_data"]["extra"]["raw_info"]
user.email = data["email"] if user.email.blank?
end
end
end
has_many :pins, :dependent => :destroy
end
devise.rb
require 'devise/orm/active_record'
require 'omniauth-facebook'
config.omniauth_path_prefix = "/users/auth"
config.omniauth :facebook, "XXXX", "XXXX", {:client_options => { :ssl => { :verify => false } }}
end
routes.rb
Dine::Application.routes.draw do
get "home/index"
resources :pins
resources :pin
resources :users
match '/auth/facebook' => 'omniauth#passthru'
devise_scope :user do
get '/users/auth/:provider' => 'users/omniauth_callbacks#passthru'
devise_for :users, :controllers => { :omniauth_callbacks => "users/omniauth_callbacks" } , :strategy_class => OmniAuth::Strategies::Facebook
match 'users/:id' => 'users#show', as: :user
root :to => 'pins#index'
get 'about' => 'pages#about'
get 'weekly' => 'pages#weekly'
get 'shop' => 'pages#shop'
get 'service' => 'pages#service'
get 'privacy' => 'pages#privacy'
get 'test' => 'pages#test'
get 'recipies' => 'pages#recipies'
match 'contact' => 'contact#new', :as => 'contact', :via => :get
match 'contact' => 'contact#create', :as => 'contact', :via => :post
end
end
OmniauthCallbacksController
class OmniauthCallbacksController < Devise::OmniauthCallbacksController
def passthru
send(params[:provider]) if providers.include?(params[:provider])
end
protected
def facebook
raise "Implement me for facebook"
end
def twitter
raise "Implement me for twitter"
end
private
def providers
["facebook", "twitter"]
end
end
UsersController
class UsersController < ApplicationController
def show
#user = User.find(params[:id])
#pins = #user.pins.page(params[:page]).per_page(20)
end
end
new.html.erb
<h2>登入</h2>
<%= simple_form_for(resource, :as => resource_name, :url => session_path(resource_name)) do |f| %>
<div class="form-inputs">
<%= f.input :email, :required => false, :autofocus => true %>
<%= f.input :password, :required => false %>
<%= f.input :remember_me, :as => :boolean if devise_mapping.rememberable? %>
</div>
<%= link_to "Sign in with Facebook", user_omniauth_authorize_path(:facebook) %>
<div class="form-actions">
<%= f.button :submit, "Sign in" %>
</div>
<% end %>
<%= render "devise/shared/links" %>
from a terminal:
bundle exec rake routes >> r.txt
open r.txt and look for the line containing users#show, note down the first term of that line, which should be something like user_show (let's call it the_route).
add _path to your route (eg. the_route_path) and search your whole project for that string.
somewhere in your project (most likely in a view) there will be a link_to or redirect_to the_route_path and I guess somewhere you will be passing 'sign_up' as a :id param.
that's where the mistake is.
I've followed the directions in devise's wiki regarding overriding update method to update user information without adding password, but I'm still getting notice saying that I can't leave password blank. I'm stuck and I need some assistance on tracking the problem down.
<%= form_for(resource, :as => resource_name, :url => custom_update_user_registration_path, :html => { :method => :put }) do |f| %>
<%= devise_error_messages! %>
<%= f.text_field :first_name %>
<%= f.text_field :last_name %>
<%= f.email_field :email, :autofocus => true %>
<% if devise_mapping.confirmable? && resource.pending_reconfirmation? %>
<div>Currently waiting confirmation for: <%= resource.unconfirmed_email %></div>
<% end %>
<%= f.submit "Update" %>
in my routes :
devise_for :user, :controllers => { :registrations => "registrations" }, :skip => [:registrations, :sessions] do
get 'signup' => 'devise/registrations#new', :as => :new_user_registration
post 'signup' => 'devise/registrations#create', :as => :custom_user_registration
get 'users/cancel' => 'devise/registrations#cancel', :as => :cancel_user_registration
get 'account/edit' => 'devise/registrations#edit', :as => :custom_edit_user_registration
put 'account' => 'devise/registrations#update', :as => :custom_update_user_registration
delete 'users/cancel' => 'devise/registrations#destroy'
# devise/sessions
get 'signin' => 'devise/sessions#new', :as => :new_user_session
post 'signin' => 'devise/sessions#create', :as => :user_session
delete 'signout' => 'devise/sessions#destroy', :as => :destroy_user_session
end
Here's my controller:
class RegistrationsController < Devise::RegistrationsController
before_filter :check_permissions, :only => [:new, :create, :cancel]
skip_before_filter :require_no_authentication
def check_permissions
authorize! :create, resource
end
def update
#user.attributes = params[:user]
# required for settings form to submit when password is left blank
if params[:user][:password].blank? && params[:user][:password_confirmation].blank?
params[:user].delete(:password)
params[:user].delete(:password_confirmation)
end
#user = User.find(current_user.id)
if #user.update_attributes(params[:user])
set_flash_message :notice, :updated
# Sign in the user bypassing validation in case his password changed
sign_in #user, :bypass => true
redirect_to after_update_path_for(#user)
else
render "edit"
end
end
end
I'm not sure why I'm getting the error that I need to submit password still? Can someone see something that I'm overlooking?
Thanks
You could handle that in your model normally.
Just edit your valdiations. This way you normally do not need to override the whole controller..just edit the model:
validates :password, :presence => true, :on => :create
I just found out that I've been overriding my devise registration incorrectly.
This was the correct way of having registration controller
Rails 3 /devise seems to ignore custom controller
Try to change
if #user.update_attributes(params[:user])
to device method
if #user.update_without_password(params[:user])
In class RegistrationsController < Devise::RegistrationsController you are missing update parameters for devise.
def configure_permitted_parameters
devise_parameter_sanitizer.for(:account_update) { |u| u.permit(:email, :password, ...) }
end
def update
# Get all params for :account_update
account_update_params = devise_parameter_sanitizer.sanitize(:account_update)
# Allow user to update without using password.
if account_update_params[:password].blank?
account_update_params.delete("password")
account_update_params.delete("password_confirmation")
end
# Set current_user
#user = User.find(current_user.id)
if #user.update_attributes(account_update_params)
set_flash_message :notice, :updated
sign_in #user, :bypass => true
redirect_to after_update_path_for(#user)
else
render "edit"
end
end
I've followed RyanB's railcast 163 on self-referential association to the letter, but I keep getting the following error:
NoMethodError in Users#show
Showing /Users/markwalker/welcomepie/app/views/shared/_suggested_connections.html.erb where line #6 raised:
undefined method `name' for nil:NilClass
Extracted source (around line #6):
3: <ul>
4: <% for friendship in #user.friendships %>
5: <li>
6: <%= h friendship.friend.name %>
7: (<%= link_to "remove", friendship, :method => :delete %>)
8: </li>
9: <% end %>
Here is my view (it is a partial that is rendered in the Users/show page):
<div class="span5">
<div class="MemberDisplay">
<ul>
<% for friendship in #user.friendships %>
<li>
<%= h friendship.friend.name %>
(<%= link_to "remove", friendship, :method => :delete %>)
</li>
<% end %>
</ul>
</div>
</div>
My friendship model:
class Friendship < ActiveRecord::Base
belongs_to :user
belongs_to :friend, :class_name => 'User'
attr_accessible :friend_id, :user_id
end
My Friendships controller:
class FriendshipsController < ApplicationController
def create
#friendship = current_user.friendships.build(:friend_id => params[:friend_id])
if #friendship.save
flash[:notice] = "Added friend."
redirect_to root_url
else
flash[:error] = "Unable to add friend."
redirect_to root_url
end
end
def destroy
#friendship = current_user.friendships.find(params[:id])
#friendship.destroy
flash[:notice] = "Removed friendship."
redirect_to current_user
end
end
My User model:
class User < ActiveRecord::Base
has_many :friendships
has_many :friends, :through => :friendships
rolify
# Include default devise modules. Others available are:
# :token_authenticatable, :confirmable,
# :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
# Setup accessible (or protected) attributes for your model
attr_accessible :role_ids, :as => :admin
attr_accessible :name, :email, :password, :password_confirmation, :remember_me
attr_accessible :occupation_list, :gender_list, :moving_from_list, :moving_to_list, :family_type_list, :age_bracket_list, :interest_list, :about_me, :username, :avatar
has_attached_file :avatar, :styles => { :medium => "300x300>", :thumb => "100x100>" }
acts_as_taggable
acts_as_taggable_on :occupation, :gender, :moving_from, :moving_to, :family_type, :age_bracket, :interests
scope :by_join_date, order("created_at DESC")
private
def self.tag_tokens(query)
tags = User.categories.where("name like ?", "%#{query}%")
if tags.empty?
[{id: "#{query}", name: "#{query}"}]
else
tags
end
end
end
My users_controller:
class UsersController < ApplicationController
before_filter :authenticate_user!
def index
authorize! :index, #user, :message => 'Not authorized as an administrator.'
#users = User.all
end
def show
#user = User.find(params[:id])
end
def new
#user = User.new
end
def create
#user = User.new(params[:user])
if #user.save
sign_in #user
redirect_to users_path, :notice => "Welcome to WelcomePie!"
else
redirect_to new_users_path, :alert => "Please try signing up again"
end
end
def edit
end
def update
authorize! :update, #user, :message => 'Not authorized as an administrator.'
#user = User.find(params[:id])
if #user.update_attributes(params[:user], :as => :admin)
redirect_to user, :notice => "User updated."
else
redirect_to user, :alert => "Unable to update user."
end
end
def destroy
authorize! :destroy, #user, :message => 'Not authorized as an administrator.'
user = User.find(params[:id])
unless user == current_user
user.destroy
redirect_to users_path, :notice => "User deleted."
else
redirect_to users_path, :notice => "Can't delete yourself."
end
end
def find_tags
#tags = Video.tag_tokens(params[:q])
respond_to do |format|
format.html
format.json { render json: #tags }
end
end
end
And my routes:
Welcomepie::Application.routes.draw do
resources :friendships
authenticated :user do
root :to => 'home#index'
end
root :to => "home#index"
devise_for :users, :controllers => { :registrations => :registrations }
resources :users
end
Does anyone have ideas about why it won't recognise friendship.friend.name? I think it has something to do with Devise, but I just can't work it out.
Thanks for any help!!
You should set :foreign_key in your Friendship model
class Friendship < ActiveRecord::Base
belongs_to :user, :foreign_key => :user_id
belongs_to :friend, :class_name => 'User', :foreign_key => :friend_id
attr_accessible :friend_id, :user_id
end
The issue was actually in the index view, even though the error was occurring in the show view.
The problem was that when I added a friend, their ID wasn't being set, it was being set to zero. It may be something to do with the tutorial being older rails syntax. Anyway, for mine to work I needed to add .id to user for it to correctly set an id, as below:
<% if user_signed_in? %>
<h3>Welcome back <%= current_user.name %>!</h3>
<% else %>
<h3>Please sign up <%= link_to 'here', new_user_registration_path %></h3>
<% end %>
<% #users.each do |user| %>
<p>User: <%=link_to user.name, #user %>
<%= link_to "Add as friend", friendships_path(:friend_id => user.id), :method => :post %> </p>
<% end %>