I'd like to write a custom method around Devise's edit profile page. I'd like it to be run before the edit page is loaded and after it is submitted. However, my code doesn't seem to be working:
class RegistrationsController < Devise::RegistrationsController
before_filter :check_tutor, only: :edit
private
def check_tutor
if current_user.is_tutor
current_user.build_tutor if current_user.tutor.nil?
else
current_user.tutor.destroy
end
end
end
Any ideas as to why this may be? Thanks!
Try adding edit action to this controller
def edit
super
end
For the filter to execute after the form is submitted, you will have to add
before_filter :check_tutor, only: [:edit, :update]
def update
super
end
For devise to pick up you controller you need to do following change in routes
devise_for :users, controllers: {registrations: "registrations"}
you might also want to consider an around_filter http://guides.rubyonrails.org/action_controller_overview.html#after-filters-and-around-filters. i'd show some sample code but i'm not sure if you're trying to build the tutor in the before and destroy it in the after [as needed] or are these actions supposed to run on both sides of the controller action.
Related
I want to override show logic in order to permit change password not only if it is expired, but some days earlier. I need to modify or replace this before_filter logic
def skip_password_change
return if !resource.nil? && resource.need_change_password?
redirect_to :root
end
I want to make my controller like this:
class PasswordsController < Devise::PasswordExpiredController
# ...
def skip_password_change
return if !resource.nil? && (resource.need_change_password? || ... )
redirect_to :root
end
end
How to achieve my goal?
UPD:
all answers below are kind of right, I missed one thing - my custom controller was placed inside controllers/admin directory, so I should name it Admin::CustomPasswordExpiredController, but I missed Namespace prefix Admin and rails fell into circular dependency.
Just extend devise controller with your custom controller:
# config/routes.rb
devise_for :users, controllers: { passwords: 'custom_passwords' }
# app/controllers/custom_passwords_controller.rb
class CustomPasswordsController < Devise::PasswordsController
def edit
resource = resource_class.new
return unless resource.need_change_password? # your middleware logic here
super
end
end
More facilities can be found in devise PasswordsController documentation (by clicking "View source")
You can try this workaround, first skip the default before_action for skip_password_change method, then add a custom method to wrap it in a condition. Try this
class PasswordsController < Devise::PasswordExpiredController
skip_before_action :skip_password_change, only: :show
before_action :skip_password_change_show, only: :show
def skip_password_change_show
return if !resource.nil? && (resource.need_change_password? || #yourcondition )
redirect_to :root
end
end
Hope that helps!
Go to your routes file and overwrite the controller methods there.
Something like devise_for :users, controllers: {x: 'y'} where x is the name of the controller from devise that you want to overwrite and y is the name of your custom controller you want to overwrite with
Is there an easy way to allow users who created their own project able to edit their work?
class Project < ActiveRecord::Base
belongs_to :user
end
class User < ActiveRecord::Base
has_many :projects
end
How do I check if the current_user that is logged in can actually edit their stuff?
If my projects URL's are something like localhost:3000/projects/24, I want only the user who created this project can go into localhost:3000/projects/24/edit to view the page and actually edit...
At this time of writing, now I'm thinking this might not be he best way? Maybe I need to somehow do localhost:3000/projects/username1/24 or something? And then if they edit, it'll be localhost:3000/projects/username1/24/edit.... How can I accomplish this?
My routes:
Rails.application.routes.draw do
devise_for :users
get 'users/:id' => 'users#show', as: :user
resources :projects
end
My controller is just basic stuff from scaffolding
In your projects controller, add devise's authentication:
before_action :authenticate_user!, only: [:edit, :update]
This will ensure that the user is logged in when trying to view the edit page.
Next, you want to ensure that the logged-in user is the owner of the project.
To do this, you'll need to modify the edit and update methods to find the project by more than just the params:
def edit
#project = current_user.projects.find(params[:id])
#...
end
def update
#project = current_user.projects.find(params[:id])
#...
end
Now, only the current_user who owns the project can view the edit page and send the updates.
Bonus points: If you want to refactor the code above and not use the same "#project = " line twice, you can create another before_action which will assign the #project for both edit and update:
before_action :authenticate_user!, only: [:edit, :update]
before_action :find_project_by_user, only: [:edit, :update]
private
def find_project_by_user
#project = current_user.projects.find(params[:id])
end
Doing this, you won't have to add the same "#project = " line into both the edit and update methods.
Maybe I need to somehow do localhost:3000/projects/username1/24 or
something? And then if they edit, it'll be
localhost:3000/projects/username1/24/edit.... How can I accomplish
this?
Since user has many projects, you would probably want the url of the type:
localhost:3000/users/1/projects/2/edit
To accomplish this you would need the following setup:
#routes.rb
resources :users, shallow: true do # notice shallow, it will eliminate users/ from the path, but as I'm not mistaken /user_id/ as well..
resources :projects
end
The controller projects should be put under:
app/controllers/users/projects_controller.rb
and it should look like
class Users
class Projects
#...
end
end
With this setup you'll ensure, that user only see his projects.
def index
#projects = Projects.all # user can see all projects
#...
end
def show
#project = Projects.find(params[:project_id])
#...
end
def edit
#project = current_user.projects.find(params[:project_id]) # can edit only those, which he associated with
# ...
end
And just make sure you are making the link_to edit visible only for the user who can edit.
As to paths:
there are two options:
resources :users, path: '' do # will hide the users part from path
#...
end
resources :users, shallow: true do # will actually also hide the users/user_id part
#...
end
What you really want is to move beyond authentication, into authorization. Authentication validates that the user is who they say they are. Authorization validates that a user is allowed to perform a specific action on a specific object. Check out the Pundit Gem.. Its really simple and easy to use.
I have simply route in my rails application that looks like this:
resources :users, only: :show
And now for example I want to redirect to http://no_present_path.com when user with sended id is not present and when user with seneded id is present redirect to http://present_path.com. Is it any way to do this with routes constraints?
Routes are meant as a simple match between a string representing a part of a url, method and an action within a controller. The best way to achieve what you're after is using a before_action in your controller. Example
class UsersController < ApplicationController
before_action :authenticate_user, only: [:show]
def show
...
end
private
def authenticate_user
redirect_to some_other_path unless id_correct?
end
end
For example I have defs: def index, def manage, def new. And in routes I have the next:
if user_signed_in?
def new
end
end
But when I wrote it other def(index and manage) also asks me login. How can I do that just new def asks me login?
routes:
get "/new" => "posts#new"
Try adding this line to the controller instead
before_filter :authenticate_user!, only: [:new]
If you are indeed using Devise please skim the guide in its entirety.The filters are what you missed.
before_filter :authenticate_user! is the way to go.
What you are doing in your question is conditionally defining the controller's actions based on a boolean of whether a user is signed in/not, which is, in essence, a wrong approach. Besides, at the point of definition of the controller class, there isn't a user.
I tried to find solution by google and here in SO but couldn't found...
This is the only question. It has only one answer and it's accepted but doesn't work for me... Here is my code:
class RegistrationsController < Devise::RegistrationsController
before_filter :authenticate_user!
def new
puts "Method new was called"
super
end
end
When I'm not logged in at localhost:3000/sign_up page is displayed normally and Method new was called is printed. I want controller to redirect me into sign_in page if I'm not already signed in. Of course I can check it in new method and redirect but it's not a good solution... I'm sure there is a more elegant way. I even tried to use prepend_before_filter :authenticate_user! but it doesn't work too.
EDIT
I've defined routes for this controller in routs.rb
devise_for :users, :controllers => { :sessions => "sessions", :registrations => "registrations" }
Devise::RegistrationsController has require_no_authentication before filter by default.
So it's needed to skip it:
class RegistrationsController < Devise::RegistrationsController
skip_before_filter :require_no_authentication
before_filter :authenticate_user!
def new
puts "Method new was called"
super
end
end
skip_before_filter :require_no_authentication
before_filter :authenticate_user!
does not work any more.
Use authenticate_scope! instead.