I have the following in my controller for Attachment
def upload
#attachment = Attachment.build(:swf_uploaded_data => params[:attachment][:attachment], :user_id => current_user.id, :project_id => params[:space_id])
....
end
What I'd like from CanCan is to only allow users to upload to a project_id they belong to. I confirmed the controller is getting the correct info, no nils
Here is my cancan:
can :upload, Attachment do |attachment|
Rails.logger.info 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX- include CanCan::Ability - ATTACHMENT'
Rails.logger.info attachment.inspect
Rails.logger.info attachment.project
current_user.try(:role, attachment.space)
end
Problem here, is that attachment. is nil, and attachment.project is nil? How do you solve for this issue with CanCan so I can make sure only project teammembers can upload attachments to the project?
Thank you
I think the best approach it to do it at a lower level with the authorize! method that the Controller action.
So ...
#AttachmentController
#Will remove it from cancan
load_and_authorize_resource :except => [:upload]
def upload
#attachment = Attachment.build(:swf_uploaded_data => params[:attachment][:attachment], :user_id => current_user.id, :project_id => params[:space_id])
#add the authorize logic explicitly here when you have the attachment model populated
authorize! :upload, #attachment
end
Let me know if that works for you.
For example if you want to allow create events for current loop only:
You use in the view
link.... if can? :create, #loop.events.new
and then in controller
skip_authorize_resource only: [:new, :create]
...
def new
#event.loop_id = #loop.id
authorize! :create, #event
end
#similar for create action
Related
I'm trying to follow wicked tutorial for creating an object partially
( https://github.com/zombocom/wicked/wiki/Building-Partial-Objects-Step-by-Step )
The problem is, I am having trouble creating the object itself. I've tried with and without strong params, or even making the call out of the controller, but can get it passed. What am I doing wrong?
class ProspectsController < ApplicationController
include Wicked::Wizard
steps :signup, :business_details, :user_details
def show
create_prospect if params[:prospect_id].nil?
byebug # => prospect_id is no appearing => Not_found
#prospect = Prospect.find(params[:prospect_id])
render_wizard
end
def update
#prospect = Prospect.find(params[:prospect_id])
params[:prospect][:status] = 'users_detailed' if step == steps.last
#prospect.update_attributes(params[:prospect])
render_wizard #prospect
end
def create_prospect
#prospect = Prospect.create
new_prospect_build_path(prospect_id: #prospect.id)
end
# def prospect_params
# params.require(:prospect).
# permit(:user_first_name, :user_last_name, :user_email, :dni, :plan, :empresa_name, :empresa_email,
# :empresa_phone, :empresa_address, :empresa_web, :empresa_category, :empresa_summary, :user_birthday,
# :user_phone, :user_address, :sex, :iban_code, :status, :prospect_id)
# end
end
Routes:
resources :prospects, only: [:show, :update] do
resources :build, controller: 'prospects'
end
you're using same controller action for two routes:
GET /prospects/:prospect_id/build/:id => prospects#show
GET /prospects/:id => prospects#show
same with update.
If you will get to that controller by GET prospect_path you will not get :prospect_id, but :id.
I'm building a basic admin rails application for a Service I use to make creating new users more efficient. Right now, I'm doing it manually, within the application, and it has some tedious steps that I'd like to automate.
The service considers users as "Members"
So I've created a Members scaffold in my rails project which has the same parameters as members do in the Service.
Instead of entering some data in the Service application, I want to do that in my app. So I have a form with several parameters that create a Member and save those parameters: :usname :usemail :usstudio_uid
Next, I want to POST to the Service API with the initial fields that were entered to create an "invitation" for the new member.
I'm trying to do that by calling on a HTTParty function in my Member's Show view.
My form is saving the parameters correctly, I'm connecting to the Service API via HTTParty and creating an invitation OK, but the Member parameters I want to send aren't populating. Instead it's turning what I thought was a reference to the parameter in plain text.
Snippet of that function:
:body => {"callback_url" => "https://foo.com/#invitations",
"consumer_key" => "my consumer_key", "email" => :usemail,
I want :usemail to reference the parameter of the Member being referenced in the show page(i.e. cliff#foo.com). Instead, as you'll see below, the Service API instead thinks the parameter is a string, returning "usemail" as the email parameter, not that of the Member object I want to reference.
I'm pretty new to rails and coding, so it's probably an obvious answer, but I spent a good 6 hours yesterday trying to figure it out. Help! :)
members.rb model:
class Member < ActiveRecord::Base
end
members_controller rb:
Class MembersController < ApplicationController
before_action :set_member, only: [:show, :edit, :update, :destroy]
respond_to :html
def index
#members = Member.all
respond_with(#members)
end
def show
#member = Member.find(params[:id])
end
def new
#member = Member.new
respond_with(#member)
end
def edit
respond_with(#member)
#member.save
end
def create
#member = Member.new(member_params)
#member.save
respond_with(#member)
end
def update
#member.update(member_params)
respond_with(#member)
end
def destroy
#member.destroy
respond_with(#member)
end
private
def set_member
#member = Member.find(params[:id])
end
def member_params
params.require(:member).permit(:usname, :usemail, :usphone, :uspassword, :usconfirm_password, :usinvitation_uid, :usstudio_uid, :uscallback_url, :usmember_uid, :ususername, :usaccess_token)
end
end
In my members_helper.rb:
def getinvitation(member)
result = HTTParty.post "https://foo.com/api/v2/studios/studio_uid/invitations", :body => {"callback_url" => "https://foo.com/#invitations",
"consumer_key" => "my consumer_key", "email" => :usemail,
"name" => :usname, "studio_uid" => :usstudio_uid
}.to_json, :headers => {'X-Auth-Token' => "my token"}
JSON.parse(result.body)
x = ActiveSupport::JSON.decode(result.body)
end
I call on the function in the member's show path:
views/members/show.html.erb
<%= getinvitation(#member) %>
Here is the response I get:
{"uid"=>"29ad0740f4aa47d788bb2a34e9ab7d78", "studio_uid"=>"ORfspJitFbVG",
"date_sent"=>"Sun Feb 1 16:49:21 2015", callback_url"=>"https://app.ustudio.com/#invitations?
invitation_uid=29ad0740f4aa47d788bb2a34e9ab7d78&studio_uid=ORfspJitFbVG",
"consumer_key"=>"my consumer_key", "email"=>"usemail", "name"=>"usname"}
Basically, this is what I was looking for
"email" => member.usemail not :usemail or "usemail"
I have everything working correctly, but now I want to limit some user Abilities to perform some attachment actions.
Specifically, the ability to limit the viewing of all uploaded attachments, to those actually uploaded by the User.
Here is the applicable snippet from ability.rb I tried ...
if user.id
can :access, :ckeditor
can [:read, :create, :destroy], Ckeditor::Picture, assetable_id: user.id
can [:read, :create, :destroy], Ckeditor::AttachmentFile, assetable_id: user.id
end
The situation arises when I am using the CKeditor UI, click the Image button, and then click the Browse Server button to see the previously uploaded images -- right now the image browser shows the uploads of all users. I would like the viewed images to be limited to those of the current_user only.
Since the Ckeditor table saves the assetable_id of the attachment (i.e. the user.id), and the logic above does not work on its own, I'm guessing some custom Controller logic is also needed here.
Thanks.
I was able to solve this issue with custom Ckeditor controllers & some guidance from here:
https://github.com/galetahub/ckeditor/issues/246
First I needed to make copies of the Ckeditor controllers pictures_controller.rb & attachment_files_controller.rb and place them here:
/app/controllers/ckeditor/
Then a few updates to their suggestions to update index were necessary, particularly picture_model.find_all needed to be picture_adapter.find_all in pictures_controller.rb (and similarly attachment_file_adapter.find_all in attachment_files_controller.rb)
The key to it all is setting the proper scope with: ckeditor_pictures_scope(assetable_id: ckeditor_current_user) & ckeditor_attachment_files_scope(assetable_id: ckeditor_current_user)
Once these revisions are in place, the file browsers for pictures & attachments show only the appropriate files for that user.
Here are the revised files ... the changes are on line 4 of both.
/app/controllers/ckeditor/pictures_controller.rb
class Ckeditor::PicturesController < Ckeditor::ApplicationController
def index
#pictures = Ckeditor.picture_adapter.find_all(ckeditor_pictures_scope(assetable_id: ckeditor_current_user))
#pictures = Ckeditor::Paginatable.new(#pictures).page(params[:page])
respond_with(#pictures, :layout => #pictures.first_page?)
end
def create
#picture = Ckeditor.picture_model.new
respond_with_asset(#picture)
end
def destroy
#picture.destroy
respond_with(#picture, :location => pictures_path)
end
protected
def find_asset
#picture = Ckeditor.picture_adapter.get!(params[:id])
end
def authorize_resource
model = (#picture || Ckeditor.picture_model)
#authorization_adapter.try(:authorize, params[:action], model)
end
end
/app/controllers/ckeditor/attachment_files_controller.rb
class Ckeditor::AttachmentFilesController < Ckeditor::ApplicationController
def index
#attachments = Ckeditor.attachment_file_adapter.find_all(ckeditor_attachment_files_scope(assetable_id: ckeditor_current_user))
#attachments = Ckeditor::Paginatable.new(#attachments).page(params[:page])
respond_with(#attachments, :layout => #attachments.first_page?)
end
def create
#attachment = Ckeditor.attachment_file_model.new
respond_with_asset(#attachment)
end
def destroy
#attachment.destroy
respond_with(#attachment, :location => attachment_files_path)
end
protected
def find_asset
#attachment = Ckeditor.attachment_file_adapter.get!(params[:id])
end
def authorize_resource
model = (#attachment || Ckeditor.attachment_file_model)
#authorization_adapter.try(:authorize, params[:action], model)
end
end
I'm trying to make it so only admins can add uses with devise. I've gotten it mostly working however now when I'm logged in as an admin and submit the sign up form it kicks me back with the error: You are already signed in.
I've tried to follow the instructions here: http://wiki.summercode.com/rails_authentication_with_devise_and_cancan but it doesn't seem to mention this situation.
Do I need to do further overriding in the editors_controller to allow this?
Here are my routes ("editors" is the name of my user model):
devise_for :admins, :skip => [:registrations]
as :admin do
get 'admin/editors' => 'editors#index', as: :admin_editors
get 'admin/editors/new' => 'editors#new', as: :new_editor
delete 'admin/editors/:id' => 'editors#destroy', as: :destroy_editor
end
devise_for :editors, :skip => [:registrations], :controllers => { :registrations => "editors" }
and my editors_controller in "app/controllers/"
class EditorsController < Devise::RegistrationsController
before_filter :check_permissions, :only => [:new, :create, :cancel]
skip_before_filter :require_no_authentication
def dashboard
render "editors/dashboard.html.haml"
end
def index
#editors = Editor.all
respond_to do |format|
format.html
end
end
private
def check_permissions
authorize! :create, resource
end
end
EDIT
I noticed this Processing by Devise::RegistrationsController#create as HTML in the logs when I submit the form. I had suspected that perhaps the skip_before_filter :require_no_authentication wasn't being called, but assumed that because the EditorsController was inheriting from RegistrationController that before filter would work properly. Is that not the case?
You'll want to implement your own create method on EditorsController instead of inheriting that action from Devise::RegistrationsController. As you're seeing, the method in Devise::RegistrationsController will first check to see if you're already logged in and kick you back if you are. If you're not logged in it will create a User account and then log you in as that user.
You're trying to get around that problem with skip_before_filter :require_no_authentication, but it's likely that your form is POSTing to /editors instead of /admin/editors. So, you'll need to add a route that allows you to get to create on the EditorsController :
as :admin do
post 'admin/editors' => 'editors#create'
# your other :admin routes here
end
Then you'd want to implement a scaled down version of create. You probably want something kind of like this :
class EditorsController < Devise::RegistrationsController
def create
build_resource(sign_up_params)
if resource.save
redirect_to admin_editors_path
else
clean_up_passwords resource
respond_with resource
end
end
# your other methods here
end
You'll also want to make sure that the admin/editors/new template is pointing the form to the correct route ('admin/editors').
None of the googleable solutions worked when I tried them. This works
What I did was create a new action in the controller and a new route for it, and connect the links on my views that normally connect to create to now call my route and action.
But that wasn't enough. Because Devise is listening and will grab any add you try to do and validate it through it's own code. So instead I just add the new user record with a sql insert.
Add this route
post 'savenew', to: 'users#savenew'
Add this action to the user controller:
def savenew
rawsql = "insert into users (email, created_at,updated_at) values ('#{user_params[:email]}',now(), now())"
sql = rawsql
ActiveRecord::Base.connection.execute(sql)
redirect_to action: 'index''
end
View: new.html.erb
change the form_for so that submit will go to the new route and action, not the default Rails one.
<%= form_for User, :url => {:action => "savenew"} do |f| %>
Using Rails 4.2.6 here (my model is User instead of Editor). The following solution bypasses (I think) any devise actions that may interfere with new User creation by the admin:
Add this action to the Users controller:
def savenew
User.create_new_user(user_params)
redirect_to action: 'index'
end
Add this private method to the Users controller if it does not exist:
private
def user_params
params.require(:user).permit(:email, :password,
:password_confirmation)
end
Add this to config/routes.rb:
match '/savenew', to: 'users#savenew', via: :post
Add this class method to the User model:
def self.create_new_user(params)
#user = User.create!(params)
end
I don't have a separate Admin class in my application. Instead, I defined an admin attribute for Users and check for it with a :validate_admin before_action filter in the UsersController.
I wanted to be able to create a new user from the :index view, so I added a button:
<%= button_to 'New User', '/new_user', class: 'btn btn-primary',
method: :get %>
You might have to tweak the above solution if you have any after_create actions in the User model (e.g. sending a welcome email).
I've been building messaging in a rails app for users to be able to send each other messages. I've looked at a few gems such as mailboxer but ultimately decided to build my own.
I'm hoping someone can help me put these pieces together. I've been following a similar question's answer here.
I'm testing in the rails console and I keep getting the following error:
undefined method `send_message' for #
How can I fix this?
Controller
class MessagesController < ApplicationController
# create a comment and bind it to an article and a user
def create
#user = User.find(params[:id])
#sender = current_user
#message = Message.send_message(#sender, #user)
flash[:success] = "Message Sent."
flash[:failure] = "There was an error saving your comment (empty comment or comment way to long)"
end
end
Routes
resources :users, :except => [ :create, :new ] do
resources :store
resources :messages, :only => [:create, :destroy]
end
Messages Model
class Message < ActiveRecord::Base
belongs_to :user
scope :sent, where(:sent => true)
scope :received, where(:sent => false)
def send_message(from, recipients)
recipients.each do |recipient|
msg = self.clone
msg.sent = false
msg.user_id = recipient
msg.save
end
self.update_attributes :user_id => from.id, :sent => true
end
end
You are invoking the method on a class level: Message.send_message. For this to work, it would expect a declaration like this:
def self.send_message(from, recipients)
# ...
end
But, you got this instead:
def send_message(from, recipients)
# ...
end
So, either invoke the method on the instance you need it for, or refactor to make it work on a class level.