Send devise confirmation email manually later - ruby-on-rails

I have added devise :confirmable to my model and created a before_create to skip the confirmation.
before_create :skip_confirmation
def skip_confirmation
self.skip_confirmation!
end
I have a mailer named store_mailer.rb along with appropriate views app/views/stores/mailer/confirmation_instroctions.html.erb to send out the confirmation email.
class StoreMailer < Devise::Mailer
helper :application # gives access to all helpers defined within `application_helper`.
include Devise::Controllers::UrlHelpers # Optional. eg. `confirmation_url`
default template_path: 'store/mailer' # to make sure that your mailer uses the devise views
end
confirmation_instroctions.html.erb
<h2>Resend confirmation instructions</h2>
<%= form_for(resource, as: resource_name, url: confirmation_path(resource_name), html: { method: :post }) do |f| %>
<%= devise_error_messages! %>
<div class="field">
<%= f.label :email %><br />
<%= f.email_field :email, autofocus: true, value: (resource.pending_reconfirmation? ? resource.unconfirmed_email : resource.email) %>
</div>
<div class="actions">
<%= f.submit "Resend confirmation instructions" %>
</div>
<% end %>
<%= render "stores/shared/links" %>
I'm trying to send the confirmation email with this:
StoreMailer.confirmation_instructions(#store).deliver
But it returns the following error: ArgumentError in TransactionsController#create
wrong number of arguments (given 1, expected 2..3)
Any ideas what might be wrong?
Update 1
transaction_controlller.rb
def create
nonce_from_the_client = params['payment_method_nonce']
#result = Braintree::Customer.create(
first_name: params['first_name'],
last_name: params['last_name'],
:payment_method_nonce => nonce_from_the_client
)
if #result.success?
puts #result.customer.id
puts #result.customer.payment_methods[0].token
StoreMailer.confirmation_instructions(#store).deliver
redirect_to showcase_index_path, notice: 'Subscribed, please check your inbox for confirmation'
else
redirect_back( fallback_location: (request.referer || root_path),
notice: "Something went wrong while processing your transaction. Please try again!")
end
end

#store.send_confirmation_instructions.deliver
This generates the confirmation token, as well as sends the mail.

From the title seems like you are trying to manually send the email later. The proposed solution in your question is just postponing the email, not exactly manually triggering it.
If you want to forbid sending the email immediately and then to send it manually you could do it like this:
class RegistrationsController
...
def create
...
resource.skip_confirmation_notification!
...
end
Some other place where you want to trigger the email, call:
User.first.send_confirmation_instructions
User.first - change with your user

confirmation_instructions is a method defined in Devise::Mailer (the superclass of your StoreMailer class).
As you can see here and here, it accepts 2 mandatory arguments and 1 optional.
You're invoking the method passing just one argument.
You must pass the second argument (token) too, like the following:
def create
# ...
# Note: I'm not sure this is the token you really need.
# It's your responsibility check if it's correct.
token = #result.customer.payment_methods.first.token
StoreMailer.confirmation_instructions(#store, token).deliver
# ...
end

Related

Rails: External API Integration using RestClient (undefined local variable or method `username')

I am building a digital library, and I have completed a lot of the functionalities needed. I am currently having an issue with integrating the digital library with a Learning Management System (LMS).
I already have an admin authentication system for the digital library using the Devise gem. My goal is to allow users who want to access the digital library to login to the digital library using their Learning Management System (LMS) credentials (username and password).
I have been provided with the Login API endpoint and other needed parameters of the Learning Management System (LMS), and I have created the User Model, the Sessions Controller and the Sessions View Templates.
I am currently using the RestClient Gem for the API call, but I having an error undefined local variable or method `username' for # Did you mean? user_path. I can't figure out where things went wrong.
Sessions Controller
require 'rest-client'
class SessionsController < ApplicationController
def new
end
def create
response = RestClient::Request.execute(
method: :post,
url: 'https://newapi.example.com/token',
payload: { 'username': "#{username}",
'password': "#{password}",
'grant_type':'password' },
headers: { apiCode: '93de0db8-333b-4f478-aa92-2b43cdb7aa9f' }
)
case response.code
when 400
flash.now[:alert] = 'Email or password is invalid'
render 'new'
when 200
session[:user_id] = user.id
redirect_to root_url, notice: 'Logged in!'
else
raise "Invalid response #{response.to_str} received."
end
end
def destroy
session[:user_id] = nil
redirect_to root_url, notice: 'Logged out!'
end
end
Sessions New View
<p id=”alert”><%= alert %></p>
<h1>Login</h1>
<%= form_tag sessions_path do %>
<div class="field">
<%= label_tag :username %>
<%= text_field_tag :username %>
</div>
<div class="field">
<%= label_tag :password %>
<%= password_field_tag :password %>
</div>
<div class="actions">
<%= submit_tag 'Login' %>
</div>
<% end %>
User Model
class User < ApplicationRecord
has_secure_password
validates :username, presence: true, uniqueness: true
end
Any form of help with code samples will be greatly appreciated. I am also open to providing more information about this integration if required. Thank you in advance.
I think that the problem is in fact that inside your SessionsController in create action, you are interpolating username and password. There's no definition for these methods in your code so you get undefined local variable or method.
You could probably pick those from params like this:
def username
params[:username]
end
def password
params[:password]
end
Or interpolate them directly in payload replacing current method calls with params[:username] and params[:password].
In such situations, it is good to use byebug or pry to debug your code and see what's happening inside your controller.
You could also think of closing some parts of your logic in Service objects - you shouldn't have more 10-15 lines in your controller action (unless the situation requires it)
Maybe you should use params[:username] rather than only username ?
username and password in payload are undefined variables. Please set their values. Possible values could be params[:username] and params[:password]

Manually Send Email Rails

I've been trying to implement the code from this question: Send an email manually of a specific page in rails app
The only difference is that I need to fetch the email address from my model. This is usually no problem when sending emails from models.
UserMailer.report(self).deliver
But I want to click on a button in the show view of my record.
I need to manually send out emails using the details of the record in the email.
Maybe there is a better approach than using an extra controller for this?
# app/mailers/user_mailer.rb
class UserMailer < ActionMailer
def report(thing)
#thing = thing
mail :to => thing.email, :from => 'you#example.com',
:subject => 'that report you want'
end
end
# app/views/user_mailer/report.html.erb
<h1>Report</h1>
<p>Here is your <% #thing.customer_id %></p>
# app/controllers/reports_controller.rb
def create
UserMailer.report(#thing).deliver
flash[:notice] = 'report sent!'
redirect_to root_path # or wherever
end
# in a view
<% form_tag(reports_path(#thing), :method => :post) do %>
<% submit_tag 'send report email' %>
<% end %>
I'm returning null with the code above:
ArgumentError (wrong number of arguments (0 for 1)):
app/controllers/reports_controller.rb:3:in `create'
Create is a post request in rails, you cannot pass parameter like this you need to fetch from params. I'm seeing you are giving it a parameter which is wrong.
Secondly you are doing #thing = thing and then you are sending thing (without #) to report method of UserMailer which is also wrong, it would be be nil in report method. you should do UserMailer.report(#thing).deliver after #thing is an object which has email

rails 4 strong params session

I am using a custom passwordless login for my app. A user simply needs to enter their username or email, and then a unique login link with a token is sent to them. They enter their username or email into a simple form :
<%= form_tag request_token_path, id:'login-form' do %>
<%= text_field_tag :user_id %>
<% end %>
This posts to a sessions#request_token method which verifies whether that user exists and then sends along the login link.
def request_token
lookup = session_params[:user_id]
if lookup.include? '#'
#user = User.find_by(email: lookup)
else
#user = User.cached_find(lookup)
end
if #user
#user.send_login_link
redirect_to login_path, notice: "#{#user.username.capitalize} your email was sent!"
else
redirect_to login_path, notice: "Whoops! Looks like #{lookup} is not registered on this site. Please check spelling or signup!"
end
end
My question is that in my SessionsController file I defined the sessions_params
private
def session_params
params.require(:session).permit(:user_id,:auth_token)
end
I know that means that I have to use a session object or in order to pass along the :user_id from the form since I defined :user_id as a param that is on valid as an attribute of a session. I am wondering the correct way to do this. Making a new session object doesn't make sense since that isn't even a model I have but is it safe to just take it from the params?
and instead make lookup = params[:user_id] ?
If you have a session object that responds to user_id attribute, you need to create the form for that object specifically:
<%= form_for #session do |f| %>
<%= f.text_field :user_id %>
<% end %>
If that's not the case, and you need to stick to form_tag, try making the attribute name something that would come up in the controller as a session hash:
<%= text_field_tag "session[user_id]" %>
When you do
params.require(:session)
it means you're requiring your params hash to have a session key, which in turn should have the permitted user_id attribute:
{params: {session: {user_id: "something"}}
And thats why you'd need form_for #session OR the textfield with the suggested "session[user_id]" name

Ruby on rails can't create with params

I have a from created in Ruby on rails. The code the form looks like this:
<%= simple_form_for(#action) do |f|%>
<%= render 'shared/error_messages' %>
<%=f.label :action_name, "Action name"%>
<%=f.text_field :action_name%></br>
<%=f.input :startDate,:as => :datetime_picker, :label =>"Start date"%>
<%=f.input :endDate,:as => :datetime_picker, :label =>"End date"%>
<%=f.label :contentURL, "Content url"%>
<%=f.text_field :contentURL%></br>
<%= f.button :submit, class: "btn btn-large btn-primary" %>
<%end%>
But when I click the submit button I get this error:
undefined method `permit' for "create":String
def action_params
params.require(:action).permit(:action_name, :startDate,:endDate,:contentURL)
All other forms a working ok, I guess it is something really obvious, just can't see it :(
I really appreciate any help, solving this problem.
Thanks!!
EDIT:
Controller code:
def create
action = Action.new(action_params)
if #action.save
flash[:success] = "New Action saved"
redirect_to "/"
else
render 'new'
end
end
private
def action_params
params.require(:action).permit(:action_name, :startDate,:endDate,:contentURL)
end
In Rails 4, you must use Strong Parameters in your controllers. Here's some explanation from the official blog. And some example:
class PeopleController < ActionController::Base
# This will raise an ActiveModel::ForbiddenAttributes exception because it's using mass assignment
# without an explicit permit step.
def create
Person.create(params[:person])
end
# This will pass with flying colors as long as there's a person key in the parameters, otherwise
# it'll raise a ActionController::MissingParameter exception, which will get caught by
# ActionController::Base and turned into that 400 Bad Request reply.
def update
redirect_to current_account.people.find(params[:id]).tap do |person|
person.update_attributes!(person_params)
end
end
private
# Using a private method to encapsulate the permissible parameters is just a good pattern
# since you'll be able to reuse the same permit list between create and update. Also, you
# can specialize this method with per-user checking of permissible attributes.
def person_params
params.required(:person).permit(:name, :age)
end
end
Notice how, in the last lines, under the private keyword, the person_params method is defined, which declares the permitted fields to be assigned by the create and update methods on top. And it's the person_params that is used for updating - the valid example - instead of the raw params array.

Editing Users With Devise and Omniauth

I'm working through the Railscast on implementing Devise and OmniAuth (along with the Devise documentation) -- currently, I've got a site going where visitors can sign up using their facebook accounts or by filling out a form.
I'm running into trouble when users that sign up via OmniAuth try to edit their profiles, though. Devise looks for the user's current password when they submit changes to their profiles, but those that logged in with facebook don't know their passwords (they're set automatically in the user model):
def self.find_for_facebook_oauth(auth, signed_in_resource=nil)
user = User.where(:provider => auth.provider, :uid => auth.uid).first
unless user
user = User.create(first_name:auth.extra.raw_info.first_name,
last_name:auth.extra.raw_info.last_name,
provider:auth.provider,
uid:auth.uid,
email:auth.info.email,
password:Devise.friendly_token[0,20]
)
end
user
end
When a user edits his information, the app should not require password confirmation if he set up his account through OmniAuth. The tutorial suggests that the handy password_required? method will help me achieve this outcome. Specifically, adding this method to the user model means that it should only return true if the user didn't sign up through OmniAuth (the provider attribute would be nil in that case):
def password_required?
super && provider.blank?
end
Thus, a piece of code like:
<%= form_for(resource, :as => resource_name, :url => registration_path(resource_name), :html => { :method => :put }) do |f| %>
<%= devise_error_messages! %>
<%= render :partial => "essential_user_info_inputs", :locals => { :f => f } %>
<%= render :partial => "inessential_user_info_inputs", :locals => { :f => f } %>
<% if f.object.password_required? %>
<%= render :partial => "password_inputs", :locals => { :f => f } %>
<%= f.label :current_password %> <i>(we need your current password to confirm your changes)</i><br />
<%= f.password_field :current_password %>
<% end %>
<%= f.submit "Update" %>
<% end %>
would theoretically only display password inputs when needed. It also suggests that Devise has built in logic saying that OmniAuth users don't need to use passwords to edit their accounts. I have no idea if this is true, but the tutorial kind of makes it look like that. But when an OmniAuth user tries to edit his account, I get "Current password can't be blank." Same thing with non-OmniAuth users (this makes sense, since the password fields don't show up on those users' edit pages either).
Some poking around confirms that the password_required? method is returning false, both when the user signed up through OmniAuth and through the site's regular user signup. Even when I change it to simply run the superclass method, it returns false.
Any ideas of what's going on with the password_required method? I can't find anything about it anywhere, but I feel like that's what's tripping things up right now.
Update:
This is now working, but not using the method outlined in the Railscast, which relies on requires_password? method, a topic that I still know nothing about. Instead, I implemented the solution outlined here, as suggested here. So I am now only requiring passwords to update non-OmniAuth accounts with the code:
class Users::RegistrationsController < Devise::RegistrationsController
def update
#user = User.find(current_user.id)
email_changed = #user.email != params[:user][:email]
is_facebook_account = !#user.provider.blank?
successfully_updated = if !is_facebook_account
#user.update_with_password(params[:user])
else
#user.update_without_password(params[:user])
end
if successfully_updated
# Sign in the user bypassing validation in case his password changed
sign_in #user, :bypass => true
redirect_to root_path
else
render "edit"
end
end
end
The easiest way is to overwrite the update_resource method in your RegistrationsController. This is advised by devise in their own implementation of the controller:
# By default we want to require a password checks on update.
# You can overwrite this method in your own RegistrationsController.
def update_resource(resource, params)
resource.update_with_password(params)
end
So the solution is to overwrite this method in your own controller like this:
class Users::RegistrationsController < Devise::RegistrationsController
# Overwrite update_resource to let users to update their user without giving their password
def update_resource(resource, params)
if current_user.provider == "facebook"
params.delete("current_password")
resource.update_without_password(params)
else
resource.update_with_password(params)
end
end
end
I've added an update to the link below that includes my solution to the Devise/ OmniAuth change user profile/password issue and collected some helpful links:
stackoverflow - Allowing users to edit accounts without saving passwords in devise
I saw this used somewhere.
def update
params[:user].delete(:current_password)
params[:user].delete(:password)
params[:user].delete(:password_confirmation)
if current_user.update_without_password(params[:user])
redirect_to somewhere_wicked_path, notice => "You rock"
else
render 'edit', :alert => 'you roll'
end
end
use something like this in your update method in your controller. Pretty sure that method is in Devise too.

Resources