rails nested devise redirect - ruby-on-rails

i have an app with nested resources. my routes are:
resources :teams do
resources :blogs
end
in my blogs controller, im using a different layout, by adding
layout "teamlayout"
to the controller.
Both layouts, the application.html.erb and the teamlayout.html.erb have included a login form itself. which i made working by this: https://github.com/plataformatec/devise/wiki/How-To:-Display-a-custom-sign_in-form-anywhere-in-your-app
now my question. when a user logs in, i want him redirected to the page from where he logs in.

You have a couple options:
Include a hidden field value in the login form that describes the source of the login (e.g. hidden_field_tag(:login_source, "team")) and define your own logic for SessionsController#create that uses the hidden field value to determine the location for response_with.
Or, you could keep track of the user's location by using a before_filter in the controllers with login forms by doing something like
def store_location
session['saved_location'] = request.request_uri
end
Then, you can override the after_sign_in_path_for(resource) method in your application controller to use the session saved_location value to determine where to redirect.
The second option seems a little less invasive to the Devise infrastructure to me, but is a little less flexible.

Related

How do you make sure that after sign in the user completes the profile form first before they can use the rest of the website functions.

How do you make sure that after sign in the user completes the profile form first before they can use the rest of the website functions. I am trying to make sure that after the member has completed the sign up form and then completes there email confirmation with devise that when they sign in that when they are redirected to the new_member_profile_path(current_member) form that they stay on this page and that if they decide to go to a link and click that that they will automatically be redirected back to the complete your profile page with the notice before please complete your profile first. I have it set already once they have completed the form they will be redirected to their member's page. I have looked in multi-forms with wicked - I really feel that because I am still am a Novice rails developer that this would be unnecessary. I am thinking about putting an if clause in the application.html.erb where the site nav template is based and putting a clause with <% if current_member_profile.blank ? %> then redirect back to new_member_profile_path(current_member) with a flash notice tag written in the html file. I have tried this if clause but does not work - comes up as undefined method. Please could someone point me in the right direction or give me the simple solution of getting this idea to work. Thanks in advance ;)
There are various approaches to achieve what you're trying to do. Perhaps the cleanest with the least amount of code needed would be to first authenticate the user with Devise's own authenticate_user! filter and then check for a field that can only be there when the profile has been filled in.
# in user.rb
def has_completed_profile?
first_name.present?
end
# in application_controller.rb
before_action :authenticate_user!
before_action :require_user_profile
private
def require_user_profile
# nothing needs to be done if the profile was already filled in
return if user_signed_in? && current_user.has_completed_profile?
redirect_to edit_profile_url, alert: "Please complete your profile first!"
return false
end
Notice how I've extracted has_completed_profile? into the User model instead of putting the name check directly into the controller. This way, if you need to change the logic of what makes a profile "complete", you don't need to touch the controller at all ("complete profile" is a business concept, not a routing/HTTP concept and thus doesn't belong in the controller).
In controllers where you don't want the additional profile check – e.g. the controller where the user actually completes their profile, where they presumably need to be logged in but can't have a profile yet – you just skip the additional filter:
# in profiles_controller.rb
skip_before_action :require_user_profile
Side note: Over the years I've learned that it's best to keep things like profile data, address data, phone numbers and what not in a separate model and don't extend Devise's User model. This prevents various issues and keeps the already huge User model (Devise includes dozens of methods into it and turns it into a God Object as it is) a bit slimmer. And if you think about it, it also makes sense in terms of business logic: A user has_one profile, has_one address (or has_many addresses) etc.
Hope that's clear.
You can add a new method in the application_controller.rb
For eg
def current_member_profile
current_user.name.blank?
end
Check the params which should not be blank when creating a member profile. I have added 'name' here for example. if the params is blank, then it will redirect as you have specified in your application.html.erb
Instead for putting an if condition on the application.html.erb, You can use a before_action in your application_controller.
Something like this.
application_controller.rb:
before_action :check_for_profile_completion
def check_for_profile_completion
// your code of redirection to the page if the profile is incomplete
end
Also you can skip this action on controller which you don't wanna restrict user to go. Like
skip_before_action :check_for_profile_completion, only: [://actions you wanna skip seperated by comma]

How can I create custom login page URLs in Rails/Devise for partner links?

I have a Rails 4 application using Devise for authentication.
In addition to the standard Devise login page, I want to have partner login pages that basically look and function the same way, but have a unique URL, like:
http://example.com/partner1
What is the best way to do this? I'm not sure how the routes file should look. And is using FriendlyID a good idea as well?
I have a PartnerPage model in the database which contains a "link" field, which is where the admin will enter the link/partner code.
Creating a custom route to the signup form is pretty straight forward:
get '/partner1', to: 'devise/sessions#new'
However if you want to have a customized login screen a good solution is to subclass Devise::SessionsController.
# app/controllers/partner1/sessions_controller.rb
class Partner1::SessionsController < Devise::SessionsController; end
and in your routes:
get '/partner1', to: 'partner1/sessions#new'
This will render app/veiws/partner1/sessions/new.html.erb.

Using 1 Controller in 2 layout

I have a logic question and I cannot figure out how to do it. First of all, I am working on an social networking site, and I completed the site in pure PHP, but now I am re-writing the backend in rails.
My questions is, I generated UsersController, it has new, create, show, edit, update, delete, and destroy.
I thought I could use "new" to display sign up page, "create" to process sign up, "show" to display profile page, "edit" to display account settings and "update" to process edit.
I might have a logic problem here, maybe I should put "new" and "create" in a signup controller. This where I get confused. The problem with the first logic I said, I have 2 layouts, one of them is for before login, and the other one is for after login. (You can imagine Facebook's header before login, and after login).
So, when I have 2 different layout, I cannot use 1 controller in 2 layout. Because sign up page has "before login header design" and account settings and profile has "after login header design". And as you can guess I define layout in controller.
I don't know if I explained well. Thank you.
By default, Rails will look-up a layout with the same name as the controller, or else application.html.erb. But you can also specify one controller-wide (which won't help you, but bear with me)
class SomethingController
layout "some_name"
...
That's still layout-wide, so not what you need.
But you can also specify a specific layout on each call to render in an action:
def edit
#some logic
render "some_template", :layout => "some_layout"
end
Or, to take the default template lookup, but still specify a layout:
def edit
# some logic
render :layout => "some_layout"
end
There's another way you can specify layouts too, which might be especially appropriate for the use case of "one layout if not logged in, another if logged in":
class SomeController
layout :method_name_to_determine_layout
# ... actions ...
protected
def method_name_to_determine_layout
if current_user
"logged_in_layout_name"
else
"not_logged_in_layout_name"
end
end
You can learn more about the different ways to specify layouts in the Layouts and Rendering Rails Guide
Hope this helps.
Rails has basic CRUD default actions. Additionally each action can have different processing depending on the HTTP verb. You can also add custom actions & routes.
It is best to follow standard Rails practices for each default action. For example, "new" action should route to the form to create a new user when accessed via GET. An HTTP POST to the form should route to the "create" action.
If you need to add an additional controller action, do so with a custom method. Again, I stress, simple CRUD actions should follow normal Rails conventions.
Read more about routing
Read this guide many times to understand simple CRUD actions in Rails
Instead of using 1 controller in 2 layouts, I decided to use separate controllers. So, I have profile_controller, which has "edit" and "update" for account settings and "show" to display profile. And I also users_controller, which has followings: login, login_attempt, signup, signup_attempt, etc..
So, I am not putting signup and edit together in 1 controller, instead using 2 different controllers is much better and clean, I guess.
Sounds like you're trying to roll your own authentication.
I'd recommend using Devise... great tutorial here:
The reason for this is two-fold.
Firstly, Devise gives you the ability to split your app between authenticated and non-authenticated users. Namely, it provides the user_signed_in?, devise_controller? and current_user helpers to aid with this.
This might not appear like a big deal, but it will actually help you with your layouts (I'll describe more in a second).
Secondly, Devise is pre-rolled. Your questions about how to handle signups and registrations have already been solved. Of course, there's nothing preventing you from making your own authentication (Devise is just built on Warden after all), but it should give you some ideas on how this has been done already.
In regards your original question (about layouts), the other answer is very good (in terms of setting layouts per method etc).
To add to it, I would say that you have to remember that Rails is a series of classes. As such, setting the layout option in the controller is the best way to ensure you're getting the correct one.
Here's Rails explanation on it:
#app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
layout :your_layout
private
def your_layout
devise_controller? || !user_signed_in? ? "new_user" : "application"
end
end
I've found it better to keep your logic as terse as possible. IE only have one set of logic for the layout. We tend to keep it in the ApplicationController, overriding where necessary.
--
Finally, your questions also highlighted a base misunderstanding of Rails. I'm not being disrespectful; I've found the clearer your perception of the framework, the better you can work with it:
You have to remember several key points with Rails:
It's an MVC (Model View Controller) framework
It's built on Ruby; hence is object orientated
maybe I should put "new" and "create" in a signup controller. This where I get confused.
If you take Devise as a model, you'll see that you could treat your controllers as layers of abstraction for objects. That is, as Devise shows us, you can have sessions and registrations controllers.
I'm not advocating you do this exactly, I am trying to show that if you put the focus onto the objects you're working with, it becomes clearer where your controller actions should be placed.
You'll also be best understanding the CRUD (Create Read Update Destroy) nature of Rails controllers & objects. The standard Rails controller is set up as such:
Whilst this is not strict, it does give you another indication as to the structure your controllers should adhere to.
You may know this stuff already!

How does Twitter get two different home pages depending on login or not? - Rails 3.1

If you go to Twitter.com when you are not logged in, you will see the marketing page with the login & registration fields.
However, if you go there when you are logged in, you will see your activity stream for your Twitter handle.
I know one way to do this in Rails is to have a home/index for the logged in users, and just use public/index.html for the marketing site. But Twitter is not using public/index.html, so I am wondering how they do it?
Is it just a simple case of having one root route, but then an if statement that displays two different pages depending on whether or not the user is logged in?
Or is there some other more fancy routing trick that I can use to do that?
If it is just an if-statement, that seems a bit hacky...no?
Thanks.
This might give a head start. http://collectiveidea.com/blog/archives/2011/05/31/user-centric-routing-in-rails-3/
By default controller methods that handle specific routes render a view that has a corresponding name (home/index route links to home controller and index action which in turn renders the view index.html.erb inside the app/views/home folder).
However, inside the controller method that handles the root request you can simply render a different view based on the fact that a user is logged in or not.
You would probably have something like this:
class HomeController
def index
...
if user_logged_in?
render "logged_in_user"
# else it will render the index view by default
end
end
end
In this case "logged_in_user" would be a different view (template or html.erb file) from the app/views/home folder.
For more information take a look here: Rails rendering guide
it can some kind of redirect for not logged in users to different controller , checking of user session can be done in before_filter

devise - Customizing the User Edit Pages

currently with devise & rails 3 there is a one page user edit page: /users/edit
I would like to split that out into sections for a better UI, something like:
/account/settings
/account/password
/account/notices
/account/disable
Also, I'd like to require the user to enter their current password when a user wants to change their password.
With devise, to make this happen, does this require a new controller, or can this all be handled with routes?
Also, currently, the edit page lives here: app/views/devise/registrations
Do you recommend adding these pages there? Or in /app/views/users ?
Thanks
You have multiple options here. I would go with the first option as it seems to more naturally fit what you are trying to do.
Override devise's registrations controller by inheriting from it, and update the corresponding views and routes. Here is what devise's site says about this:
Configuring controllers
If the customization at the views
level is not enough, you can customize
each controller by following these
steps:
1) Create your custom controller, for
example a Admins::SessionsController:
class Admins::SessionsController < Devise::SessionsController
end
2) Tell the router to use this
controller:
devise_for :admins, :controllers => { :sessions => "admins/sessions" }
3) And since we changed the
controller, it won’t use the
"devise/sessions" views, so remember
to copy "devise/sessions" to
"admin/sessions".
Remember that Devise uses flash
messages to let users know if sign in
was successful or failed. Devise
expects your application to call
"flash[:notice]" and "flash[:alert]"
as appropriate.
Use the User controller and add actions there with corresponding views (not my choice)

Resources