Show username on Mailboxer inbox in Rails - ruby-on-rails

I'm using the Mailboxer (https://github.com/ging/mailboxer) gem in my Rails app to handle private messages between users. As per the documentation, I'm using = render mailbox.inbox to show a list of all conversations (messages) for the logged-in user. However, the method only outputs the subject of the message. I need to show the sender of the message along with the subject. How can I edit this output so that the sender name is displayed?
The SQL table shows that the sender name is not stored in the conversations table, but on the notifications table via notifications_on_conversation_id (one to many). I tried to use <%= conversation.notifications.sender_id %> but I got the following error:
NoMethodError in Conversations#index
undefined method `notifications' for nil:NilClass
What can I add to the conversation controller and view in order to display the conversation's user and subject?
Thanks!
Conversations controller:
class ConversationsController < ApplicationController
before_filter :authenticate_user!
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

sender_id is available using receipt.notification.sender_id and .sender_type
for any conversation you can retrieve the receipts using:
#receipts = conversation.receipts_for

I ran on the same issue at some point and my solution was something like this: accessing current_user in mailboxer mailer notification

Related

Add custom attributes in the mailboxer gem

I have used mailboxer gem for the messaging between users in my website.
Which is based on spree module.
I want to add the product id with the conversation so user can filter the conversations according to the products.
How can I achieve this?
Here is the code for message_controller file:
module Spree
module Admin
class MessagesController < Spree::BaseController
before_action :authenticate_spree_user!
def index
filtered_messages = Mailboxer::Message.ads_id(params[:ad_id])
end
def new
#chosen_recipient = Spree::User.find_by(id: params[:to].to_i) if params[:to]
end
def create
product_id = params[:product_id]
recipients = Spree::User.where(id: params['recipients'])
conversation = current_spree_user.send_message(recipients, params[:message][:body], params[:message][:subject], params[:product_id]).conversation
flash[:success] = 'Message has been sent!'
redirect_to '/admin/conversations'
end
private
def conversation_params
#params.require(:mailboxer_conversations).permit(:ad_id)
params.permit(:ad_id)
end
end
end
end

Under what file do you write twilio code in order to input instance variables?

So the way that my app works is that a user enters a message, and a phone number to send that message to. The app sends the message using the Twilio gem. I'm trying to figure out how to call the variables that are stored from the user input into my twilio code.
messages_controller.rb
class MessagesController < ApplicationController
before_action :set_message, only: [:show, :edit, :update, :destroy]
def index
#message = Message.all
end
def show
end
def new
#message = Message.new
end
def edit
end
def create
#message = Message.create(message_params)
#message.send_sms(message_params)
if #message.save
redirect_to #message
else
flash[:alert] = "Contact not send message!"
end
end
def update
if #message.update(message_params)
redirect_to #message
else
render :edit
end
end
def destroy
#message.destroy
redirect_to messages_url
end
private
# Use callbacks to share common setup or constraints between actions.
def set_message
#message = Message.find(params[:id])
end
# Never trust parameters from the scary internet, only allowset_message the white list through.
def message_params
params.require(:message).permit(:number, :text)
end
end
twilio code
require 'twilio-ruby'
def send_sms(text)
account_sid = ENV["TWILIO_SID"]
auth_token = ENV["TWILIO_TOKEN"]
#client = Twilio::REST::Client.new account_sid, auth_token
message = #client.account.messages.create(:body => #message.text,
:to => #message.number,
:from => ENV["FROM"])
rescue Twilio::REST::RequestError => e
end
I would suggest to split that code into different files.
Let's start with your controller. Your create method looks like you want to send a message via Trello and that you want to store the message in your database too. I would write the controller's create method like this:
def create
#message = Message.new(message_params)
if #message.save_and_send # Note the name of the method
redirect_to #message
else
flash[:alert] = "Contact not send message!"
# render `new` again if this wasn't successful and
# show errors on the form (if exist).
render :new
end
end
In a next step I introduce a save_and_send method in message.rb that validates and sends the message and returns true if both was successful:
def save_and_send
save! if valid? && SmsGateway.send(self)
end
This method first validates the record. If it is valid it tries to send the message. And if the message was successfully send, it saves the record using save! (because at this point it should not fail).
I added the new model SmsGateway, because IMHO it is a good practise to encapsulate third party code into wrappers, because that makes it easier to maintain and to change the SMS provider in the future. The SmsGateway class is pretty simeple:
# in app/models/sms_gateway.rb
require 'twilio-ruby'
class sms_gateway
def self.send(message)
new.send(message)
end
def initialize
#client = Twilio::REST::Client.new(ENV["TWILIO_SID"], ENV["TWILIO_TOKEN"])
end
def send(message)
client.account.messages.create(
body: message.text,
to: message.number,
from: ENV["FROM"])
)
rescue Twilio::REST::RequestError => e
Rails.logger.error("SMS ERROR: #{e.message}")
message.errors.add(:base, "Sending the message failed: #{e.message}")
false
end
private
attr_reader :client
end
In the rescue block of the SmsGateway I write an error to the message instance if there the message was not be able to send and I return false. This false ensures in Message#save_and_send that the message itself is not stored in the database. And that we are able to show proper error messages back to the user.
In case anyone was curious, the answer is that you put the method in the controller under private. You call it simply by
def create
send_sms(message_params)
end
Also, the argument in the method should be message_params as well.
I did it!!!!!

Rails: Why is only one email sent to the last member of a loop?

I am very much a rails novice!
I am trying to write a method for a kind of on-line committee meeting. There are a fixed number(9) of users. When a user proposes a topic for discussion and/or voting the submit button needs to send an email to all members.
in app/mailers/user_mailer.rb I have:-
class UserMailer < ApplicationMailer
def new_topic_alert(topic)
#users = User.all
#users.each do |user|
mail to: user.email, subject: "New topic alert"
end
end
end
as part of app/controllers/topics_controller.rb I have:-
def send_alert
#topic = Topic.new(topic_params)
UserMailer.new_topic_alert(#topic).deliver_now
end
and:-
def create
#topic = Topic.new(topic_params)
if #topic.save
send_alert
flash[:info] = "New Topic alert emails sent."
redirect_to root_url
else
render 'new'
end
end
Please, why does the loop in user_mailer only send an email to the final person of the list. By incorporating "byebug" I have shown that it goes through all the user emails.
Try like below:
def send_alert
#topic = Topic.new(topic_params)
users = User.all
users.each do |u|
UserMailer.new_topic_alert(#topic, u).deliver_now
end
end
and update the mailer like
class UserMailer < ApplicationMailer
def new_topic_alert(topic,user)
mail to: user.email, subject: "New topic alert"
end
end

Adding search function for gem mailboxer

I'm new to Rails, and I found this gem mailboxer for sending messages between the website users. I am unable to write the search functions for inbox and sent.
conversations controller
class ConversationsController < ApplicationController
before_action :authenticate_user!
def new
end
def create
recipients = User.where(id: conversation_params[:recipients])
conversation = current_user.send_message(recipients, conversation_params[:body], conversation_params[:subject]).conversation
flash[:success] = "Your message was successfully sent!"
redirect_to conversation_path(conversation)
end
def show
#receipts = conversation.receipts_for(current_user)
# mark conversation as read
conversation.mark_as_read(current_user)
end
def reply
current_user.reply_to_conversation(conversation, message_params[:body])
flash[:notice] = "Your reply message was successfully sent!"
redirect_to conversation_path(conversation)
end
def trash
conversation.move_to_trash(current_user)
redirect_to mailbox_inbox_path
end
def untrash
conversation.untrash(current_user)
redirect_to mailbox_inbox_path
end
private
def conversation_params
params.require(:conversation).permit(:subject, :body,recipients:[])
end
def message_params
params.require(:message).permit(:body, :subject)
end
end
What do I need to do to implement these?
I am not aware of the gem you are talking about. However, in general, you could do something like this in Rails.
Assuming you have a model Message, with name as a column, you could do this:
Message.where("name like ?", params[:name])
where params[:name] could be either "inbox" or "sent".

Mailboxer Gem, Admin View

I'm using the mailboxer gem for conversations/messages between the User model in my app. This is all working fine, thanks to some great help in stack overflow. I'm now trying to setup a section so admin can view all the conversations that are happening.
I have created a controller and view for conversations, nested in my admin section. I've pulled in all conversations on the index page with:
def index
#admin_conversations = Conversation.all
end
This has listed all the conversations, and a link to show each conversation, as expected.
The problem I am having, is that the mailboxer Gem is setup to only allow the current_user to view conversations that the current_user is a participant in. So I can click some of the conversations, (signed is as an admin) and see the contents, but some (that are between other test users) I cannot see i.e it throws an exception such as:
Couldn't find Conversation with id=5 [WHERE "notifications"."type" = 'Message' AND "receipts"."receiver_id" = 35 AND "receipts"."receiver_type" = 'User']
How can I define the method in my admin controller so that the admin can see everything?
I'm currently using cancan and allowing all 3 user roles I have (admin, client and supplier) like this:
can :manage, Conversation
...so it isn't a normal authorisation problem.
Here is my conversations 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 :back, :notice => "Message Sent! You can view it in 'My Messages'."
end
def count
current_user.mailbox.receipts.where({:is_read => false}).count(:id, :distinct => true).to_s
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
The answer is probably something pretty silly, but I am new to this...
Thanks
In your conversation method, your calling mailbox.conversations.find(params[:id])
mailbox.conversations is what is limiting you to the current user's conversations.
Try just Conversation.find(params[:id])

Resources