Rails Device and has_many association with promotions privilege - ruby-on-rails

I have two models User and Promotion, an user can create has_many promotion and an promotion belong to user,
for the users i used devise so:
when I delete a promotion, I would like that the users can't delete also the promotions of other users but only their own
I have to change the controller but how? i hope in a help
this is controller for the destroy of a promotion
def destroy
#promotion = Promotion.find(params[:id])
#promotion.destroy
##promotion = current_user.promotions.destroy
respond_to do |format|
format.html { redirect_to promotions_url, notice:'Promotion was successfully delete.' }
format.json { head :ok }
end
end
end
sorry for my english please!

Crosscheck if the current_user is also the creator of the #promotion:
def destroy
#promotion = Promotion.find(params[:id])
if #promotion.user == current_user #if user is the owner of that promotion
#promotion.destroy
respond_to do |format|
format.html { redirect_to promotions_url, notice:'Promotion was successfully delete.' }
format.json { head :ok }
end
else
redirect_to root_path
end
end

Related

how to stop url manupulation in rails app with devise gem?

i am using devise gem to handle user. users has one_to_many association with projects. and there is multiple user each user has their own dashboard but still user are able to manipulate project_id in url and able to see other users project and also able to edit delete that. how can i stop that?
user redirection after login (project#index) -
project_controller.rb
def index
#projects = current_user.projects.all.order("created_at DESC").paginate(page: params[:page], per_page: 15)
end
def show
#project = Project.includes(stages: {tasks:}).find(params[:id])
#stages = #project.stages
end
def new
#project = current_user.projects.build
end
def create
#project = current_user.projects.build(project_params)
respond_to do |format|
if #project.save
format.html { redirect_to projects_url, notice: 'Project was successfully created.' }
format.json { render :show, status: :created, location: #project }
else
format.html { render :new }
format.json { render json: #project.errors, status: :unprocessable_entity }
end
end
end
You can simply use current_user.projects scope in show action:
def show
#project = current_user.projects.includes(stages: :tasks).find(params[:id])
end
This way, if you edit URL and put ID belonging to another user, you'll get ActiveRecord::RecordNotFound, which is handled as 404 error by Rails by default.
Of course, you can use this approach to secure edit, update and destroy actions as well.

New method in a model or new service - which approach is better?

I am implementing a new app with following business process:
User fill in a registration form.
Once registration form is being saved, a new Training Company is created
User get an e-mail with a pdf with his registration form and unique url
User uses unique url to attach signed registration form Admin can accept or reject
What is the best approach to Training Company creation?
First solution: new service that creates a TrainingCompany
class TrainingCompanyService
def initialize(company_name)
#name = company_name
end
def create_new_training_company
TrainingCompany.new(company_name: #name).save
end
end
create action in RegistrationFormController:
def create
#registration_form = RegistrationForm.new(registration_form_params)
respond_to do |format|
if #registration_form.save
format.html { redirect_to #registration_form, notice: 'Registration form was successfully created.' }
TrainingCompanyService.new(#registration_form.company_name).create_new_training_company
RegistrationFormMailer.with(registration_form: #registration_form).after_registration_email.deliver_later
else
format.html { render :new }
end
end
end
Second solution: new method inside TrainingCompany model:
class RegistrationForm < ApplicationRecord
belongs_to :training_company, optional: true
has_one_attached :registration_form
has_secure_token :signed_form_upload_token
def create_new_training_company
TrainingCompany.new(company_name: self.company_name, registration_form_id: self.id).save
end
end
create action in RegistrationFormController:
def create
#registration_form = RegistrationForm.new(registration_form_params)
respond_to do |format|
if #registration_form.save && #registration_form.create_new_training_company
format.html { redirect_to #registration_form, notice: 'Registration form was successfully created.' }
RegistrationFormMailer.with(registration_form: #registration_form).after_registration_email.deliver_later
else
format.html { render :new }
end
end
end
Which solution would you choose and why? Personally i prefer the second one (new method inside the model)...

Deleting group of elements in rails

I have an Event model and every event has user_id, and i want to delete all of them by one click.
def destroy
#events.destroy
respond_to do |format|
format.html { redirect_to events_url, notice: 'Event was successfully destroyed.' }
format.json { head :no_content }
end
end
I know that i need firstly to find all of them by user_id, but don't know how to delete than. Can someone help?
I hope your user model have has_many :events Association if this is the case then you can try following.
def destroy
user = User.find(1) # Or User.find(params[:user_id])
#events = user.events.where(title: "FIRST")
# Or If you just wanted to delete all Events except deleting user events then #events = Event.where(title: "FIRST")
#events.destroy_all
respond_to do |format|
format.html { redirect_to events_url, notice: 'Event was successfully destroyed.' }
format.json { head :no_content }
end
end
You can use destroy_all(conditions = nil). Code would look something like this:
def destroy
respond_to do |format|
if Event.destroy_all(:user_id => params[:user_id])
format.html { redirect_to events_url, notice: 'Events were successfully destroyed.' }
else
format.html { redirect_to events_url, notice: 'Events could not be deleted. Try again' }
end
end
end
Or instead of: Event.destroy_all(:user_id => params[:user_id]), if you have User object then you can do:
#user.events.destroy_all
destroy(single object)/:destroy_all(for collection) The associated objects are destroyed alongside this object by calling their destroy method
delete(single object)/:delete_all(for collection) All associated objects are destroyed immediately without calling their :destroy method
You can use any one of them according to your conditions
Event.destroy_all(conditions)
To destroy all methods with specific conditions.
Event.destroy_all(:user_id => params['user_id'])
or
Event.where(:user_id => id).destroy_all
This will destroy all events which belongs to that user_id

Can't associate post to Devise user

I'm having extreme difficulty associating post to a user registered in devise.
I generated a post scaffold and got everything set up correctly in Devise.
I added a migration to the post that included a user_id field
The user model has_many :posts
The Post model belongs_to :user
For some reason I cannot connect the user with the post. Am I missing something?
thanks all!
My controller for posts
def create
#user = User.find(params[:id])
#post = #user.posts.create(params[:post])
respond_to do |format|
if #post.save
format.html { redirect_to #post, notice: 'Post was successfully created.' }
format.json { render action: 'show', status: :created, location: #post }
else
format.html { render action: 'new' }
format.json { render json: #post.errors, status: :unprocessable_entity }
end
end
end
First of all you need a user to associate a post:
#user = User.find(params[:id]) # or just use current_user as you are using Devise
As long as you have has_many association you can do the following:
#post = #user.posts.build(params[:post]) # to return newly created object without saving it to the database
#post = #user.posts.create(params[:post]) # to create and save record to the database
That's it.

user can only delete/edit what he/she posted? not all posts

How do I make it so the user can only delete/edit what he/she posted? and not all posts? My current songs_controller only has authorization which allows users to edit, destroy, update once they're signed in. The problem is, all users can edit all posts. That said, how can I allow just the user to edit his/her own posts? and not have access to that functionality with others posts?
songs_controller.rb
class SongsController < ApplicationController
before_action :set_song, only: [:show, :edit, :update, :destroy]
before_filter :authorize, only: [:create ,:edit, :update, :destroy]
# GET /Songs
# GET /Songs.json
def index
#songs = Song.all
end
# GET /Songs/1
# GET /Songs/1.json
def show
end
# GET /Songs/new
def new
#song = Song.new
end
# GET /Songs/1/edit
def edit
end
# POST /Songs
# POST /Songs.json
def create
#song = Song.new(song_params)
respond_to do |format|
if #song.save
format.html { redirect_to #song, notice: 'Song was successfully created.' }
format.json { render action: 'show', status: :created, location: #song }
else
format.html { render action: 'new' }
format.json { render json: #song.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /Songs/1
# PATCH/PUT /Songs/1.json
def update
respond_to do |format|
if #song.update(Song_params)
format.html { redirect_to #song, notice: 'Song was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: #song.errors, status: :unprocessable_entity }
end
end
end
# Song /Songs/1
# Song /Songs/1.json
def destroy
#song.destroy
respond_to do |format|
format.html { redirect_to songs_url }
format.json { head :no_content }
end
end
private
def set_song
#song = Song.find(params[:id])
end
def song_params
params.require(:song).permit(:title, :artist, :bio, :track)
end
end
You most likely have some sort of User model that users are able to authenticate to. Try adding a has_many :songs association on your User model. Add a foreign key called user_id on the Song model along with a belongs_to :user. Migrate. Pull the user's id from the current_user helper and do the following:
#user = User.find(current_user.id)
#songs = #user.songs #will give you only the songs the user added
Here is a good guide to reference:
http://guides.rubyonrails.org/association_basics.html
if you only want the user to see the posts they have made then what jbearden suggests would work well although it doesn't prevent someone from accessing the delete or update etc from the address line manually which is bad.
if you want the user to see all songs but only have the option to delete etc on their own songs then you probably want to have the view only show the edit and delete links for songs owned by the user (which would use jbearden's idea of makign an association for the songs to users) - that helps with the UI but still doesn't solve your authentication problem.
the authentication can be handled by using the cancan gem (see railscasts on this - Ryan is the author of the gem). cancan takes some getting used to for configuring it but works quite well for controlling whether a given user can view, edit, delete etc objects (like your songs).
good luck!

Resources