How to trigger modal when event occurs in Rails - ruby-on-rails

I'm sure that there is an answer to this out there, but I'm not entirely certain how to properly phrase this question, so my apologies if this is repetitious.
I am working on implementing a badge/achievement system for a site. The backend stuff is there, but I'm working on the front-end now, and I'm basically trying to figure out how to redirect to a sort of "Congratulations!" page when someone gets a new badge.
The congratulations page is going to be a modal, but for simplicities sake, if anyone has an idea of how to trigger an action like this only once when a new Badge is created, that would be a huge help. Right now, when a user performs a given action, say... adding money to their account, a user_badge is created (adds a Badge ID to an array).
Thanks in advance!

You can use before_filter for actions, where you want to check that new badge appears (probably you don't want to check it in auth actions, so you can't use global before_filter). Then you can use redirect_to in this before filter, if user has a new badge.
But I don't recommend to do so, since you'll break the users flow. It's better to show alert/modal on the next requested page. To achieve this you can define a new instance variable in your before_filter (like #show_modal) and include the partial with modal to your footer.
example:
#before filter
def check_for_new_badges
if current_user.has_new_badges?
#show_modal = true
current_user.set_badges_as_seen!
end
end
#included in layout
- if #show_modal
= render 'shared/congrats_modal'

Related

In rails 4.2, how to display a form for preview but ensure it cannot be submitted

I'd like to have a a form view that can, depending on circumstances, have submit functionality disabled in a bullet-proof way so that even a clever user could not edit the HTML source (via a browser extension) to re-add the submit button.
It seems one way to do that might be to somehow inject an invalid authenticity token that replaces the (valid) rails-generated one, so that even if a user somehow re-adds the submit button (by editing the HTML via a browser extension) it would still be an invalid submission.
My thought is to have some logic in the view:
- if #form_disabled # set by controller
- somehow_invalidate_the_authenticity_token?
How might one 'break' Rails form submission?
The purpose of doing this, instead of rendering the preview in a :show action, is to have the exact same view displaying both the live-form and the dead-form.
If I were you, I would use pundit.
It's pretty simple, and has few lines of code if you need to know how it works.
I'd start to write the code here, but I realize that the example at the readme fit your needs.
At the application controller add this
At the folder app/policies put the class PostPolicy, of course, you must replace "Post" with the name of your controller in singular (even if you have not a model with that name). The update? (and create?) actions should return true/false to indicate if user is allowed or not.
A few lines down on the readme, you will find the PostsController#update action, which call to authorize with the record before the update. I think you want do the same with create (then you need a create? method at the policy class).
Pundit needs current_user controller method, if you don't have it. Just follow the user customization instructions.
Of course, new and edit actions don't call authorize because they are allowed to everybody. Only the POST & the PUT/PATCH actions are forbidden.
Yes, it's more than a surgery of one line of code. But it's simple and the right way of give access to users.
After reading my other answer, I start thinking that you can do the same that Pundit does at the controller:
def update
if <unauthorized user>
flash[:alert] = "You are not authorized to perform this action."
redirect_to(request.referrer || root_path)
else
# all the update stuff
# ...
end
end

Unable to log out and redirect to new page from yielded layout

In a 3.2.16 Rails app using Devise, we allow users to stay logged in for a number of days. This means, of course, that if they click a link to our page (say in their bookmarks) they come right back into the app (assuming their session is still active).
For our main screen, we have a yielding layout
...
<body>
...
<%= yield %>
...
</body>
The layout surrounding the yield includes a display of the username among other things.
And now I have a new controller:
class AccountSelectionsController < ApplicationController
def new
if user_signed_in?
sign_out current_user
current_user = nil
end
...
render :layout => "external"
end
...
end
When the new action is invoked, I want the user signed out completely, the session cleared, and the user taken to a completely different layout. The use-case assumes the user is reaching this controller from a link in, say, an email or a page outside my app (IOW, not from a spot inside my app).
I first thought I merely had do a sign_out current_user(as above), but that didn't do anything obvious: the user seems to stay signed in.
The above was just my starting point. I've tried just sign_out (without a resource, implying all scopes), reset session, and redirect_to destroy_user_session_path (which is what our standard logout button does, a button positioned on the surrounding layout).
What I got though was my new external view (or the normal new session sign in screen, depending on the permutation of what I tried) trying to render inside the old layout (as if it was part of the yield).
I could try the Devise after_sign_out_path_for to help with redirect, but then I'd only want it if it was tied to this particular controller and action and I'm not quite sure how to safely accomplish that. And now I'm not convinced it wouldn't just keep me wrapped in the surrounding layout anyway.
So, (1) is there a reason the main layout stays intact even upon a full redirect_to (even using :status => 301) that I should be able to defeat (for instance, is the yield interfering?), or (2) am I on the right track with Devise after_sign_out_path_for and what do I need to do to limit that behavior to just respond to this one controller action?
Thank you!
Richard
UPDATE: the served page (via view source) shows the intended screen body is wrapped within the layout of the origin screen
UPDATE 2: I've also tried returning a head :reset_content from a before filter along with various other things in a before_filter. Still the old layout keeps rendering before it attempts to render the new page. This is although I'm using different Chrome tabs in the test (i.e., the session stays in memory); I've tried it in Firefox too. Same result. The output of rails s shows the redirects and gives no indication that it's attempting to go through another controller first, something is triggering the layout. Is there away to force a layout in a redirect?
Try this instead,
sign_out current_user, :bypass => true
So this is my penance for posting the question.
I just figured out that a before_filter was intercepting the call to the controller and redirecting it to the wrong layout before a sign-in was ever checked for. Normally this is desired for this particular application, but I didn't realize that the filter actually was catching the redirect ahead of my controller (the logs suggested it happened at a later point). Once I set that filter to be skipped in my controller, all was well.
Moral of the story, I need to better consider the side effects of before_filters in the ApplicationController.
Thank you to RSB and Jasdeep Singh and everyone else who spent time considering an answer for this.

Ruby on Rails - Adding a second (extremely) simple Auth layer in app

Once a user logs into their account, they are presented with a list of 'Employees'.
As of right now, when you click an employee, it takes the user to the 'show' page of that specific employee, however I want to add a 'pin-protected' aspect to that list before it renders the show page.
I want to add a simple layer of authentication that would go like this:
When a user clicks their name on a list, a text-field appears that asks for the selected employee's pin.
The user types in the pin. On submit, it compares the inputted pin against the 'pin' column for that employees' record. If it's correct it grants access to the selected employee's show page.
Is this something that is easily done in RoR? This is the first real app I have worked on, so I am having trouble wrapping my mind around a couple concepts like these.
Thanks so much!
Take a look at devise, it's most definitely your best bet for Ruby on Rails 3 authentication layer.
You're best bet if you just want to add a little functionality to your existing model class would be to add a method along the lines of:
def validate_pin(pin_to_check)
self.pin == pin_to_check
end
And then you just need to modify your employee controller so that show method checks to see if the pin has been provided (ideally via a session variable), otherwise redirect and request the pin with an additional method and route Employee#request_pin in the controller which asks the user to enter the pin, on success redirecting to the Employee#show route.
Session handling in the controller
To write the session variable, you'd need an Employee#check_pin method (as a POST route) and you'd just use the code:
session[:pin_valid] = true
Then you'd check session[:pin_valid] in your Employee#show method

How to persist data about what page you are on in Rails app

This will probably be easiest if I explain what I'm trying to do. I have three actions in my Rails app controller, each rendering a different page. The page-render is done with a single partial which uses variables that were set in the controller action code. For example, each page has a list on it, but on one page the list is sortable. Up to now I've been handling this by setting a #sortable flag to true or false in the code for my actions.
This works fine when an action is initially run. The problem is that I have AJAX stuff going on (e.g. adding a new element to the list) and when this happens, I need to know the value of the #sortable variable again. It seems to have gone, even though I'm still technically on the same page. What I want is a variable store that is linked to the page you are on.
What are your recommendations for doing this? (Storing it in the Rails session hash seems like overkill - too much chance that the wrong value will get left in there by some yet-to-be-implemented action.)
Ben
In rails I've only managed to set page scoped variables for initial setup too.
I think the only solution would be to pass the sortable flag from the page on the ajax request. You can store it either with a javascript variable, in a hidden field, custom attribute on your list or anyway you wish and then in the ajax you simply add that to the request so you can treat that on the server side persistently.
Why do you don't want use session? As for me before_filter works fine for such tasks
in ApplicationController
before_filter :init_actions
def init_actions
session[:action] = action_name
session[:controller] = controller_name
end

RAILS: I've got an action that calls itself, when the form of the action's view is being sent, now I want to rout out of that action

this time it's about Ruby On Rails.
I have a form in the view "progress.html.erb" and an action called "progress". When the form is sent, the action "progress" is called again.
This must be like that, because I'm asking the user several questions by an automatic system.
Then, when the questioning is finished and the user is done with one seminar of questions, I want to route out of "progress" to "finishing" (where session data is erased and a "happy wishes"-site is shown).
But this won't work because of that routing error. There must be a way, even just rendering won't work :(
The complete system is the following:
I have a box with different panels.
In these panels are cards with questions.
All card get asked to the user.
When all panels evaluate to empty, the user is done.
Please help me!
Yours,
Joern.
When you want to redirect, and then stop rendering, you need to not reach the end of control of the function. The way I do this (for redirecting when someone is somewhere they are not supposed to be, usually) is:
redirect_to(:finishing) and return
optionally, you can do this conditionally:
redirect_to(:finishing) and return if #survey.completed?
I'm not sure what routing error you're getting. Your question/problem isn't very clear. However, it sounds like you want to redirect_to(:finishing) at the end of #progress when the user is "done".

Resources