Making Multi-user Contact Manager with Rails, devise and MongoDB - ruby-on-rails

I'm working on simple contact manager that supports multi-user. I'm using with Rails, Device, and MongoDB with MongoID.
With devise, I easily attached basic login functionality with devise, and used embedded_in for attaching contact model to user model.
However, I can't make user's dashboard page(and RESTful API endpoint) that displays user's contact list. I should make every users' contact list private to each user.
EDIT : I followed RailsApp tutorial, and this is what I've done so far. I have index page that displays every users' list, and detail view for adding contact data.
The problem is, when user sign up, every user can see everyone's contact. I want to restrict every user should only see their contact list.
And one more thing, I want to unify API endpoint for every user like:
http://domain.tld/contact.json for logged in user's contact.

Devise will give you this helper current_user. So you can do this in your controller.
# dashboard_controller.rb
before_filter :authenticate_user!
def index
#contacts = current_user.contacts
end
Then in your view
# dashboard/index.html.erb
<% #contacts.each do |contact| %>
<p>
<strong><%= contact.name %></strong> <br />
Phone: <%= contact.phone %>
Mobile: <%= contact.mobile %>
Email: <%= contact.email %>
</p>
<% end %>
Is that what you're after?

Related

Ruby on Rails Action Cable: Change display / message based on permissions of receiving user?

I have a Ruby on Rails application for which I am implementing ActionCable. (For full disclosure, I'm an advanced beginner at RoR and a complete noob with ActionCable. I'm using this app to learn about it.) I'm trying to figure out if I can do something like the following:
Imagine your standard chat room (like in all the ActionCable tutorials), with the twist being that:
users can edit their messages after they are sent, and
some of the people in the room have special permissions (you can think of them as admin users). These admin users have the ability to edit messages sent by other people after they have been sent.
When rendering the page, I have a partial for each message that looks something like this:
# View:
<%= render :partial=>"message_line", :collection=>#messages, :locals=>{:current_user=>#user}%>
# _message_line.html.erb partial
<div><%= message_line %></div>
<div>
<% if current_user.admin or current_user.id==message_line.user.id %>
<%= Link to edit post... %>
<% end %>
</div>
I have successfully set up the ActionCable such that when a user enters a message that message gets broadcast and displayed on the screens of all users in that room. But I can't figure out how tell, when receiving a message, if the user receiving it is an admin user and therefore should be shown the "link to edit post" link. The user that's invoking the controller action to push the message to everyone else is not the user receiving the message, and so therefore the controller doesn't know if the receiving user is an admin (especially given that there are multiple recipients).
As a concrete example, consider the following setup:
There are three users, UserA, UserB, UserC in the chat room. UserA is an admin, UserB and UserC are not.
Here's what should happen:
UserA enters a new message. It is broadcast to all 3 users and all 3 see it displayed on their screen. UserA sees the link to edit the message, UserB and UserC do not.
UserB enters a new message. It is broadcast to all 3 users and all 3 see it displayed on their screen. UserB and UserA sees the link to edit the message, UserC does not.
Thanks in advance for any help!
Based off this answer it looks like your partial is being rendered before it is sent. This means current_user in your partial is the sending user, not the viewing user, like you might expect.
I'd suggest doing the same thing here. Have two different partials that you render and then use the viewing user's permissions to determine which to use.
# controller
data[:htmlAdmin] = ApplicationController.render partial: 'partial1', locals: { message: message_line, admin: true }
data[:htmlUser] = ApplicationController.render partial: 'partial2', locals: { message: message_line, admin: false }
# partial1
<div><%= message_line %></div>
<div>
<%= Link to edit post... %>
</div>
# partial2
<div><%= message_line %></div>
# channel
received(data) {
if current_user.is_admin?
$("#messages_div").prepend(data[:htmlAdmin].html)
else
$("#messages_div").prepend(data[:htmlUser].html)
end
}
Edit
If you use Devise, you can get current_user in ActionCable this way:
# app/channels/application_cable/connection.rb
module ApplicationCable
class Connection < ActionCable::Connection::Base
identified_by :current_user
def connect
self.current_user = find_verified_user
end
protected
def find_verified_user
if current_user = env["warden"].user
current_user
else
reject_unauthorized_connection
end
end
end
end

Custom Button Action in Rails View - Ruby on Rails

Rails newbie here. I have an array of hashes that contain Facebook Friends that I'd like to optionally turn into 'Contacts'. I have an index view that lists all of the users's 'friends'. I'd like to have a custom button that enables the user to turn each friend into a contact. How do I do this in Rails?
Index view - I am currently listing each user with their avatar & name. I'd like to add the button in each list item.
Note: I do not have a Friend model, as I am simply pulling this data in from the Facebook Graph API as an array of hashes.
<% #friends.each do |friend|%>
<li> <%= image_tag(fb_avatar(friend["id"], "type=square"))%> <%=friend["name"]%> #put button here </li>
<% end %>
Custom make_friend_contact method in Application Helper (to be called on a friend):
def make_friend_contact
Contact.new(name: self["name"], uid: self["id"], avatar_url: fb_avatar(self["id"], "type=square"), user_id: params[:user])
end
Any help is much appreciated. I've done quite a bit of reading, and I can't seem to crack this one. Thanks!
One solution might be to use button_to. You can check it out in the Rails API. Here is one of their examples:
<%= button_to [:make_happy, #user] do %>
Make happy <strong><%= #user.name %></strong>
<% end %>
# => "<form method="post" action="/users/1/make_happy" class="button_to">
# <div>
# <button type="submit">
# Make happy <strong><%= #user.name %></strong>
# </button>
# </div>
# </form>"
where the button calls the make_happy action in the Users controller on #user.
You could do something similar by making an action in your Users controller that creates a new Contact, using a friend parameter.
def make_friend_contact
friend = params[:friend]
Contact.new(name: friend["name"]...
end
Then you just have to send the friend parameter with the button's POST request.

Show link if session user matches certain criteria

I'm trying to make a specific link in my application only visible to users who have the attribute :department equal to "Sales".
In other words, I've got a model Users, in which a user has a password, username, and department. The session saves the :user_id once the user is logged in.
What I would like to do is when my view is rendered, depending on the :department of the logged in user, either display, or don't display a specific link.
Here's the code I've got in my view, but I'm struggling with how to take the session info and find the department of the user from it.
<% if Users.where(id: session[:user_id])[:department] == "Sales" %>
<%= link_to 'New Request', new_request_path %>
<% else nil %>
<% end %>
I know it's bad to do a query anywhere other than the controller or model, so if you have any advice on how to better structure this logic as well, I would appreciate it.
I think what you want is:
<% user = User.find_by_id(session[:user_id]) %>
<% if user.present? && user[:department] == "Sales" %>
<%= link_to 'New Request', new_request_path %>
<% end %>
Personally, I'd put this into a helper method to clean it up:
In app/helpers/users_helper.rb:
def user_in_sales?
user = User.find_by_id(session[:user_id])
user.present? && user[:department] == "Sales"
end
Then your view:
<% if user_in_sales? %>
<%= link_to 'New Request', new_request_path %>
<% end %>
Personally, I'd strongly look into using something like cancan to handle this situation. I think you may find that you could use cancan as an effective authorization tool other places in your app, especially if you're doing logic like this elsewhere.
First, you're using an object oriented language. It would help you to quit getting hung up on implementation details (e.g. department == "Sales") and instead consider the intent or meaning you're trying to express and code to meet that. For example:
if current_user.works_in?(:jewelry)
link_to 'Request Receipt', new_request_path
end
Your models should expose a public interface that allows other objects in your code (like your controller) to get the information they need (i.e. whether or not a user has an association with a department) without knowledge of or concern for the underlying data storage schema.
class User
def works_in?(department_name)
departments.pluck(:name).include?(department_name.to_s)
end
end

How can I use rails- devise to sign up multiple users at once? [duplicate]

I'm trying to user Devise invitable to add multiple users at once. Basically when someone creates an account, the first thing they'll want to do is add a bunch of users using the invitable form...
just not sure how to duplicate fields in the form and have them send a create request for each entry.
Thanks in advance!
This is how I would do it.
Provide a text area that accepts a comma-separated email list. Define
a new action that sends an invitation to each email in this list.
Lets assume a User model and a users controller for this example.
Define a route for the batch_invite action.
resources :users do
collection do
post 'batch_invite'
end
end
Define the batch_invite action in the users_controller
def batch_invite
#Validate the user_emails field isn't blank and emails are valid
params[:user_emails].split(",").each do |email|
User.invite!(:email => email)
end
#redirect_to appropriate path
end
A form that accepts a comma-separated list of emails in a textarea.
<%= form_tag batch_invite_users_path, :method => :post do %>
<%= label_tag "Email ids of people you'd like to invite." %>
<%= text_area_tag :user_emails %>
<%= submit_tag "Invite!" %>
<% end %>
A couple of notes :
If you like your controller skinny, you could move the logic to the model, for instance, by creating a send_batch_invitations method in your User model and pass the params[:user_emails] as an argument to that method from the users controller.
Since the method that sends the invitations could take sufficient time to complete, I would suggest you assign this task to a background job processor, such as delayed_job or resque.
There are railscasts that demonstrate the usage of these two background job processors.

Devise invitable batch invite - Ruby on rails

I'm trying to user Devise invitable to add multiple users at once. Basically when someone creates an account, the first thing they'll want to do is add a bunch of users using the invitable form...
just not sure how to duplicate fields in the form and have them send a create request for each entry.
Thanks in advance!
This is how I would do it.
Provide a text area that accepts a comma-separated email list. Define
a new action that sends an invitation to each email in this list.
Lets assume a User model and a users controller for this example.
Define a route for the batch_invite action.
resources :users do
collection do
post 'batch_invite'
end
end
Define the batch_invite action in the users_controller
def batch_invite
#Validate the user_emails field isn't blank and emails are valid
params[:user_emails].split(",").each do |email|
User.invite!(:email => email)
end
#redirect_to appropriate path
end
A form that accepts a comma-separated list of emails in a textarea.
<%= form_tag batch_invite_users_path, :method => :post do %>
<%= label_tag "Email ids of people you'd like to invite." %>
<%= text_area_tag :user_emails %>
<%= submit_tag "Invite!" %>
<% end %>
A couple of notes :
If you like your controller skinny, you could move the logic to the model, for instance, by creating a send_batch_invitations method in your User model and pass the params[:user_emails] as an argument to that method from the users controller.
Since the method that sends the invitations could take sufficient time to complete, I would suggest you assign this task to a background job processor, such as delayed_job or resque.
There are railscasts that demonstrate the usage of these two background job processors.

Resources