Rails Mailboxer - Create Conversation from Button - ruby-on-rails

I'm using the mailboxer gem to implement user messaging on an app. Currently, i've got it set up based on this https://github.com/RKushnir/mailboxer-app. Users can enter an email address, subject and message into a form, and start a conversation.
I'd like a user to be able to start a conversation without having to enter another users email address - for instance clicking a button on a relevant page where I can pass a #user in as the recipient.
Here is my current controller:
class ConversationsController < ApplicationController
authorize_resource
helper_method :mailbox, :conversation
def create
recipient_emails = conversation_params(:recipients).split(',')
recipients = User.where(email: recipient_emails).all
conversation = current_user.
send_message(recipients, *conversation_params(:body, :subject)).conversation
redirect_to conversation
end
def reply
current_user.reply_to_conversation(conversation, *message_params(:body, :subject))
redirect_to conversation
end
def trash
conversation.move_to_trash(current_user)
redirect_to :conversations
end
def untrash
conversation.untrash(current_user)
redirect_to :conversations
end
private
def mailbox
#mailbox ||= current_user.mailbox
end
def conversation
#conversation ||= mailbox.conversations.find(params[:id])
end
def conversation_params(*keys)
fetch_params(:conversation, *keys)
end
def message_params(*keys)
fetch_params(:message, *keys)
end
def fetch_params(key, *subkeys)
params[key].instance_eval do
case subkeys.size
when 0 then self
when 1 then self[subkeys.first]
else subkeys.map{|k| self[k] }
end
end
end
end
and corresponding _form:
<%= form_for :conversation, url: :conversations do |f| %>
<%= f.text_field :recipients %>
<%= f.text_field :subject %>
<%= f.text_field :body %>
<div class="form-actions">
<%= f.button :submit, class: 'btn-primary' %>
<%= submit_tag 'Cancel', type: :reset, class: 'btn btn-danger' %>
</div>
<% end %>
I've got all the views and actions working relating to replying to conversations with messages, display all users conversations etc, but just not how to start it without having to type the address in.
Thanks for any help
Added: I don't mind editing the above form etc and losing the capability for a user to enter another's email, as I don't want them to be able to do that. The buttons in question will be named along the lines of 'ask the client a question', or 'ask the venue a question', rather than just sending messages to random users.
EDITED TO ADD:
Thanks for that, makes sense and I am nearly there...
I've ended up adding a copy of the original conversation form i'm using (in my _form file):
<%= form_for :conversation, url: :conversations do |f| %>
<%= f.text_field :recipients %>
<%= f.text_field :subject %>
<%= f.text_field :body %>
<div class="form-actions">
<%= f.button :submit, class: 'btn-primary' %>
<%= submit_tag 'Cancel', type: :reset, class: 'btn btn-danger' %>
</div>
<% end %>
This works fine from the correct view that I need it in, and does everything as expected - however, when I try to hide the recipients text field and pass in the users email address (rather than having to manually type it in, I am getting problems:
<%= form_for :conversation, url: :conversations do |f| %>
<%= hidden_field_tag :recipients, "#{#enquiry.client.user.email}" %>
<%= f.text_field :subject %>
<%= f.text_field :body %>
<div class="form-actions">
<%= f.button :submit, class: 'btn-primary' %>
<%= submit_tag 'Cancel', type: :reset, class: 'btn btn-danger' %>
</div>
<% end %>
This is throwing an error in the controller:
undefined method `split' for nil:NilClass
def create
recipient_emails = conversation_params(:recipients).split(',')
recipients = User.where(email: recipient_emails).all
conversation = current_user.
If I 'view source' on Firefox before I hit submit on the form, I can see that there is a field there with the users email address passed in correctly, but it's just not going to the controller for some reason?
Something that may help - If I change the hidden_field_tag back to a normal field, and pass in the email address as above, the view won't even load. It is saying
'undefined method `merge' for "user#user.com":String
and highlighting that row in the code.
Thanks

In mailboxer, you do not send a message to an email address, rather you send it to a model which has acts_as_messageable on it, such as user. The following code is a rough implementation of how I have done it, with the #user variable being set in the controller, as this is on the profile page.
<%= form_tag("/messages/send_message", method: "post", url: send_message_path) do %>
<%= hidden_field_tag :user_id, "#{#user.id}" %>
<%= text_field_tag :subject, nil, :class => 'form-control', :placeholder => "Subject" %>
<%= text_area_tag :message, nil, :class => 'form-control', :placeholder => "Message" %>
<%= submit_tag "Submit", :class => "btn btn-primary" %>
<% end %>
This would have an action you messages controller like this:
def send_message
#user = User.find(params[:user_id])
#message = params[:message]
#subject = params[:subject]
current_user.send_message(#user, "#{#message}", "#{#subject}")
redirect_to root_path
end
Or, in your case, if you wanted email addresses, you can just find the recipients by there email address which you have in the database:
def send_message
#user = User.find_by_email(params[:user_email])
#message = params[:message]
#subject = params[:subject]
current_user.send_message(#user, "#{#message}", "#{#subject}")
redirect_to root_path
end
And something like this in you routes file:
post '/messages/send_message', :to => "messages#send_message", :as => "send_message"
Really hope this helps!

Related

Submit values with link_to in Rails 5.1

So currently i have a link_to, where signed in users can click on:
<%= link_to "Enroll", [#task.project, #task] %>
The user has an association with the project, through subscription. To create a new subscription for a user with a project, i wrote some simple form for it.
<%= form_for([#project, #subzz]) do |f| %>
<%= f.hidden_field :project_id, :value => #project.id %>
<%= f.hidden_field :user_id, :value => current_user.id %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
Which works fine and creates the association. However, i want that the user is able to create the subscription whenever he clicks on 'enroll' instead of a second, extra submit button.
Any ideas how to approach this? I thought about using jQuery, but not sure how to inject the ids with it and if its the 'right' way to do it.
Thanks in advance everyone!
EDIT:
When using the method posted as answer, i get:
param is missing or the value is empty: sub
My updatet form:
<%= form_for([#project, #subzz], html: {role: "form", id: "project_form"}) do |f| %>
<%= hidden_field_tag :project_id, :value => #project.id %>
<%= hidden_field_tag :user_id, :value => current_user.id %>
<%= link_to "Enroll", [#task.project, #task], :onclick => "$('#project_form').submit() "%>
<% end %>
subs_controller.rb
class SubsController < ApplicationController
def create
#subz = Sub.create(sub_params)
project = #subz.project
redirect_to root_path
end
private
def sub_params
params.require(:sub).permit(:project_id, :user_id)
end
end
You can be using the existing form and link_to, just edit some like edit the dorm_tag like this
<%= form_for([#project, #subzz], html: {role: "form", id: "project_form"}) do |f| %>
and remove the button into form like this one
<div class="actions">
<%= f.submit %>
</div>
and edit the link_to like this
<%= link_to 'Enroll', "", :onclick => "$('#project_form').submit()" %>
it will work
Update
You can achieve this without a form, comment out this form and edit the link like below
<%= link_to 'Enroll', subs_path(project_id: #project.id, user_id: current_user.id), method: :post %>
and the create method update like below
def create
#subz = Sub.new(sub_params)
if #subz.save
flash[:success] = 'Sub was successfully submited.'
redirect_to root_path
else
flash[:danger] = 'Sub not submited'
redirect_to request.referer
end
end
that is easier
Or if you keep before one with form then the link out from the form and the create method edit like the following
def create
#subz = Sub.new(sub_params)
if #subz.save
flash[:success] = 'Sub was successfully submited.'
redirect_to root_path
else
flash[:danger] = 'Sub not submited'
redirect_to request.referer
end
end
and the form will look like this
<%= form_for([#project, #subzz], html: {role: "form", id: "project_form"}) do |f| %>
<%= hidden_field_tag :project_id, :value => #project.id %>
<%= hidden_field_tag :user_id, :value => current_user.id %>
<% end %>
<%= link_to "Enroll", [#task.project, #task], :onclick => "$('#project_form').submit() "%>
if you confused this [#task.project, #task] on link tag then use direct link
So i came up with the following solution:
I've added the sub handling to the application_controller, so that its availiable for the project_controller. I also added the project, tasks as a reference, so that i am able to redirect to a task via the sub_controller, instead of the project_controller.
application_controller.rb
def create
#subs = Sub.new(sub_params)
project = #subs.project
taskz = project.tasks.first
if #subs.save
redirect_to [taskz.project, taskz]
end
end
private
def sub_params
params.require(:sub).permit(:project_id, :user_id)
end
Inside the show.html.erb from the project_controller, i use the old form:
<%= form_for([#project, #subz] do |f| %>
<%= f.hidden_field :project_id, :value => #project.id %>
<%= f.hidden_field :user_id, :value => current_user.id %>
<%= f.submit "Submitted" %>
<% end %>
which works fine. Thanks for any previous help!

Ruby / ActionMailer / Saving mail

I am creating a customer support app where clients can create, view, edit and comment support tickets.
I have this portion of the app working fine, but I want to have the data they submit into the ticket form emailed to me.
I have a separate "contact us" form that emails the data to me perfectly, but I want to combine the two forms into one.
It should work like this: client creates ticket, ticket is saved into the database, a copy of the ticket is emailed to me.
I can't figure out how to make all of these actions happen from one form.
Here is my tickets controller:
class TicketsController < ApplicationController
def new
#ticket = Ticket.new
end
def create
#ticket = Ticket.new(ticket_params)
#ticket.save
redirect_to #ticket
end
def show
#ticket = Ticket.find(params[:id])
end
def index
#tickets = Ticket.all
end
def edit
#ticket = Ticket.find(params[:id])
end
def update
#ticket = Ticket.find(params[:id])
if #ticket.update(ticket_params)
redirect_to #ticket
else
render 'edit'
end
end
def destroy
#ticket = Ticket.find(params[:id])
#ticket.destroy
redirect_to tickets_path
end
private
def ticket_params
params.require(:ticket).permit(:name, :email, :phone, :help)
end
end
Here is my new ticket view:
<%= link_to "View an Existing Ticket", tickets_path, :class =>'btn btn-danger btn-sm'%>
<h1>New Ticket</h1>
<%= form_for :ticket, url: tickets_path do |f| %>
<p>
<%= f.label "Name:" %>
<%= f.text_field :name %>
</p>
<p>
<%= f.label "Email:" %>
<%= f.text_field :email %>
</p>
<p>
<%= f.label :"Phone #:" %>
<%= f.text_field :phone %>
</p>
<p>
<%= f.label :"How can we help?" %>
<p><%= f.text_area :help, :cols=> 38, :rows => 8 %></p>
</p>
<p>
<button type="submit" class="btn btn-danger btn-sm">Submit Ticket</button>
</p>
<% end %>
<p><%= button_to "Back", root_path, :class => "btn btn-danger btn-sm", :method => :get %></p>
Here is my email controller:
class ContactController < ApplicationController
def new
#message = Message.new
end
def create
#message = Message.new(params[:message])
if #message.valid?
NotificationsMailer.new_message(#message).deliver
redirect_to(root_path, :notice => "Message was successfully sent.")
else
flash.now.alert = "Please fill all fields."
render :new
end
end
end
Here is my email view:
<%= form_for #message, :url => contactcreate_path do |form| %>
<fieldset class="fields">
<div class="field">
<%= form.label :name %>
<%= form.text_field :name %>
</div>
<div class="field">
<%= form.label :email %>
<%= form.text_field :email %>
</div>
<div class="field">
<%= form.label :subject %>
<%= form.text_field :subject %>
</div>
<div class="field">
<%= form.label :body %>
<%= form.text_area :body %>
</div>
</fieldset>
<fieldset class="actions">
<%= form.submit "Send" %>
</fieldset>
<% end %>
Ticket Model:
class Ticket < ActiveRecord::Base
after_create :send_new_ticket_to_email
private
def send_new_ticket_to_email
NotificationsMailer.send_new_ticket(self).deliver
end
end
Notifications Mailer:
class NotificationsMailer < ActionMailer::Base
def send_new_ticket(ticket)
#ticket = ticket
mail(:subject => "HelpDesk: #{message.subject}")
default :from => "HelpDeskApp#ascendstudioslive.com"
default :to => "Support#ascendstudioslive.com"
end
Let me know if there is anything else you would like to see. Basically, I want to have one form that saves a ticket to the database and then emails a copy of it out.
Thank you!
You can create an after_create callback in your Ticket model to e-mail the saved ticket to yourself.
class Ticket < ActiveRecord::Base
after_create :send_new_ticket_to_email
private
def send_new_ticket_to_email
UserMailer.send_new_ticket(self).deliver
end
end
and in your ActionMailer class:
class UserMailer < ActionMailer::Base
def send_new_ticket(ticket)
#ticket = ticket
/* here you configure the variables for your email */
mail(to: your#email.com, subject: 'New ticket...')
end
end
then you will be able to use the #ticket object in your mailer views whatever way you please.
Have you tried this? I don't know if it is going to work, but all the same:
#new_message = NotificationsMailer.new_message(#message)
save_to_db(#new_message) # custom method to write it into your db somehow
#new_message.deliver
Not really sure what a contact is versus a ticket, but, broadly, the way to do what you want is, in your create action:
def create
#message = Message.create(params[:message])) # create will save and validate the message
if #message.valid?
NotificationsMailer.new_message(#message).deliver # #message gets set to
# else...
end

Rails mailboxer gem cannot send message

I am using Mailboxer gem for the messaging function, but as I am very new I am not very sure how to use it. Basically I created the following message controller:
class MessagesController < ApplicationController
before_action :set_message, only: [:new, :create]
def new
#message = Message.new
end
def create
current_user.send_message(#recipient, message_params(:body, :subject))
redirect_to conversations_path
end
private
def set_message
#recipient = User.find(params[:recipient_id])
end
def message_params
params.require(:message).permit(:body, :subject)
end
end
Then my view:
<h1>New Message</h1>
<%= simple_form_for(#message, html: {class: "form-horizontal"}) do |f| %>
<%= f.error_notification %>
<%= f.input :subject %>
<%= f.input :body, as: :text, input_html: { rows: "3"} %>
<div class="form-actions">
<%= f.button :submit, class: "btn btn-primary" %>
</div>
<% end %>
But I can't send message...
(BTW I can send message is the console, and also replace part of the message controller with "current_user.send_message(#recipient, "test", "test")", but definitely not what I want)
Use form_tag instead of form_for
Instead of using form_for, try using form_tag, as this has worked for me:
<%= form_tag("/messages/send_message", method: "post", url: send_message_path) do %>
<%= hidden_field_tag :user_id, "#{#user.id}" %>
<%= text_field_tag :subject, nil, :class => 'form-control', :placeholder => "Subject" %>
<%= text_area_tag :message, nil, :class => 'form-control', :placeholder => "Message" %>
<%= submit_tag "Submit", :class => "btn btn-primary" %>
<% end %>
This would have an action you messages controller like this:
def send_message
#user = User.find(params[:user_id])
#message = params[:message]
#subject = params[:subject]
current_user.send_message(#user, "#{#message}", "#{#subject}")
redirect_to root_path
end
And something like this in you routes file:
post '/messages/send_message', :to => "messages#send_message", :as => "send_message"
Really hope this helps!

Rails nested form, attributes not getting passed

So I have a Conversation model, which has_many messages. I'm trying to create a new message when I create a conversation. Here's my ConversationsController:
class ConversationsController < ApplicationController
before_filter :authenticate_user!
def new
recipient = User.find(params[:user])
#conversation = Conversation.new(from: current_user, to: recipient)
#conversation.messages.build(from: current_user, to: recipient)
end
def create
#conversation = Conversation.create(params[:conversation])
redirect_to #conversation
end
end
And here's my form (conversations/new.html.erb):
<%= form_for #conversation do |f| %>
<%= f.fields_for :messages do |g| %>
<%= g.label :subject %>
<%= g.text_field :subject %>
<%= g.label :content %>
<%= g.text_field :content %>
<% end %>
<%= f.submit "Send" %>
<% end %>
The problem: when I submit the form, the conversation's message gets saved, but the to and from fields that I specified as parameters in build are not saved (they are nil). However, the subject and content fields filled out in this form are saved just fine.
I've done a little bit of digging... if I do a puts on #conversation.messages in the new action, or in the new.html.erb, the message seems to have to and from. It's only when the message reaches the create action do those fields disappear.
UPDATED:
class ConversationsController < ApplicationController
before_filter :authenticate_user!
def new
recipient = User.find(params[:user])
#conversation = Conversation.new(to: recipient)
#conversation.messages.build
end
def create
#conversation = current_user.conversations.build(params[:conversation])
# Set all the attributes for conversation and messages which
# should not be left up to the user.
#conversation.to = current_user
#conversation.messages.each do |message|
message.to = #conversation.to
message.from = #conversation.from
end
redirect_to #conversation
end
end
<%= form_for #conversation do |f| %>
<%= f.hidden_field :recipient %>
<%= f.fields_for :messages do |g| %>
<%= g.label :subject %>
<%= g.text_field :subject %>
<%= g.label :content %>
<%= g.text_field :content %>
<% end %>
<%= f.submit "Send" %>
<% end %>
You may still want to validate the recipient in your Conversation model.

How to create three records using one form? And how to show validation errors for them?

Bassicaly my problem what to do if i have 3 forms and one submit button.
I want to create a form which sends email to each recipient and then create new record in free_registration_coupons table.
I need validation of email for this form.
Model FreeRegistrationCoupon: recipient_email, token, sender_id
For now i have this:
class FreeRegistrationCouponsController < ApplicationController
def send_invitations
emails = [params[:recipient_email_1], params[:recipient_email_2], params[:recipient_email_3]]
emails.reject!{ |e| e.eql?("") }
if emails.present?
emails.each do |e|
FreeRegistrationCoupon.create(:recipient_email => e, :sender_id => current_user.id)
#MAILER
end
redirect_to root_path, :notice => "You just send #{emails.size} invitations!"
else
redirect_to(:back)
end
end
end
class FreeRegistrationCoupon < ActiveRecord::Base
before_save :generate_token
attr_accessor :recipient_email, :sender_id
validates :recipient_email, :presence => true, :email => true
def generate_token
self.token = SecureRandom.hex
end
end
This is form which is in other controller CarsController#confirm:
<%= form_tag :controller => 'free_registration_coupons', :action => "send_invitations" do %>
<!-- errors -->
<%= label_tag :recipient_email_1 %>
<%= text_field_tag :recipient_email_1 %>
<%= label_tag :recipient_email_2 %>
<%= text_field_tag :recipient_email_2 %>
<%= label_tag :recipient_email_3 %>
<%= text_field_tag :recipient_email_3 %>
<%= submit_tag %>
<% end %>
I think you should have defined your form using:
<%= form_tag :controller => 'free_registration_coupons', :action => "send_invitations" do %>
<%= #error_message %>
<%= label_tag "recipient_email[1]" %>
<%= text_field_tag "recipient_email[1]" %>
<%= label_tag "recipient_email[2]" %>
<%= text_field_tag "recipient_email[2]" %>
<%= label_tag "recipient_email[3]" %>
<%= text_field_tag "recipient_email[3]" %>
<%= submit_tag %>
<% end %>
This way it will be easier to treat all email address on your controller and you can track those errors to display them afterwards:
class FreeRegistrationCouponsController < ApplicationController
def send_invitations
emails = params[:recipient_email]
emails.reject!{ |param, value| value.eql?("") }
errors = []
if emails.any?
emails.each do |param, value|
validation_result = FreeRegistrationCoupon.save(:recipient_email => value, :sender_id => current_user.id)
#MAILER
end
redirect_to root_path, :notice => "You just send #{emails.size} invitations!"
else
#error_message = "You have to include, at least, one e-mail address!"
render :name_of_the_action_that_called_send_invitations
end
end
end
I didnt test this code. Hope it helps!

Resources