Add ProfileImage from users previously uploaded photos - ruby-on-rails

I have tried to do this but was unsuccessful. There's also no postings covering this which is odd considering selecting Default photo is a option for every social network. I have the CarrierWave gem and I want to set it up so that the user can select their ProfileImage (default image) from photos they have already uploaded. This photo would be used site wide. It's like having an avatar, however the articles out there only show how to upload a avatar as oppose to selecting a avatar from your uploaded photos. I am sure this will be helpful to other people since this is a common feature.
Photos controller:
def new
#photo = Photo.new
end
def create
#photo = Photo.new(params[:photo])
#photo.user = current_user
if #photo.save
flash[:notice] = "Successfully created photos."
redirect_to :back
else
render :action => 'new'
end
end
def edit
#photo = Photo.find(params[:id])
end
def update
#photo = Photo.find(params[:id])
if #photo.update_attributes(paramas[:photo])
flash[:notice] = "Successfully updated photo."
redirect_to #photo.gallery
else
render :action => 'edit'
end
end
def destroy
#photo = Photo.find(params[:id])
#photo.destroy
flash[:notice] = "Successfully destroyed photo."
redirect_to #photo.gallery
end
end
User model:
# It is setup so no gallery is created, and photos are associated with the user.
private
def setup_gallery
Gallery.create(user: self)
end
Photo model:
attr_accessible :title, :body, :gallery_id, :name, :image, :remote_image_url
belongs_to :gallery
has_many :gallery_users, :through => :gallery, :source => :user
belongs_to :user
mount_uploader :image, ImageUploader
LIMIT = 5
validate do |record|
record.validate_photo_quota
end
def validate_photo_quota
return unless self.user
if self.user.photos(:reload).count >= LIMIT
errors.add(:base, :exceeded_quota)
end
end
end

You could set up the user model to be linked directly to a default photo.
class User < ActiveRecord::Base
belongs_to :default_photo, :class_name => "Photo"
end
You'd also need to add a default_photo_id column to the users table.
Then provide an interface that allows the user to browse all of their photos. In the UI you could have a button that says "Make default" (or whatever), and when the user clicks on that button it triggers a controller action that looks something like this:
def choose_default_photo
#photo = Photo.find params[:photo_id]
current_user.default_photo = #photo
redirect_to '/profile' # or wherever you wan to send them
end
Then whenever you need to reference the model for the default photo you'd just use:
current_user.defaut_photo

You should also take care of scenario when you destroy the default image . you should set default_photo_id to nil or to any other photo if you want.

Related

How to return object from db but still needing to render errors, rails

I have a Micropost model and a Gallery model and a Picture model and the pictures either belong to Micropost or Gallery model. I have a micropost form where it accepts_nested_attributes_for :pictures and have set the associations in models, and, controller micropost create action and strong parameters look like this,
Micropost_controller
def create
#micropost = current_user.microposts.create(micropost_params)
#pictures = #micropost.pictures
#pictures.each do |pic|
pic.update!(gallery_id: params[:gallery_id])
end
...
render 'static_pages/home'
end
def micropost_params
params.require(:micropost).permit(:content, pictures_attributes: [:id, :gal_refs_id, :picture, :_destroy]))
end
The gallery is preset so I have its id which is sent in a hidden field and retrieved in the microposts controller as shown.
Problem is I need to return the #micropost object so I can update the :gallery_id, so I cannot use #micropost.save as it only returns true or false and this is the behavior I need to re-render the form if errors occur.
At the moment using create it silently fails and renders the template.
How can I overcome this or is there a better way to do this?
Thanks in advance!
Try using the below code. If #micropost has no errors, it will create #micropost and you can update its associated objects
def create
#micropost = current_user.microposts.build(micropost_params)
if #micropost.save
# if the object has no errors
#pictures = #micropost.pictures
#pictures.each do |pic|
pic.update!(gallery_id: params[:gallery_id])
end
...
render 'static_pages/home'
else
# show Errors
#micropost.errors
end
end
def micropost_params
params.require(:micropost).permit(:content, pictures_attributes: [:id, :gal_refs_id, :picture, :_destroy]))
end
Models:
class Micropost
has_many :pictures, as: :imageable, inverse_of: :imageable
end
class Picture
belongs_to :imageable, polymorphic: true
end
class Gallery
has_many :pictures, as: :imageable, inverse_of: :imageable
end
# Note: inverse_of is important here! It allows you to instantiate the
# Picture object without having a Picture object yet.
Controller:
def create
#micropost = micropost.new(micropost_params)
#pictures.each do |picture|
#micropost.pictures.new({
picture: picture,
gallery_id: params[:gallery_id]
})
end
# Note: Pictures will not be saved or persisted in the database until
# .save is called.
# Note: If your Picture object is invalid, Micropost save will also fail
if #micropost.save
# Saved, do something here with the success response
render 'static_pages/home'
else
# Errors
#micropost.errors
end
end
Picture Validation:
This is code from my production app, adjust to your needs.
steam_game_details['images'].each do |image|
image_file_name = image.split('/').last.split('?').first
local_picture = picture.where({
picture_file_name: image_file_name
}).first_or_initialize
# Check if the image is nil and whether or not it actually exists on AWS S3
#
if local_picture.picture.nil? || !local_picture.picture.exists?
local_picture.assign_attributes({
picture: open(URI.parse(image)),
picture_file_name: image_file_name # Overwrite the name generated by Paperclip
})
end
end

customize gallery so that it belongs to one user

I am using CarrierWave and as of now the gallery is open to the public with no ownership. I want to setup so that for one, the user does not have to create a Gallery. The only option should be to upload photos to their account and I want to limit each user photo uploads to 5 maximum. So if User 16 signs in, they have option to upload up to 5 photos to their profile. Once that limit is reach, if the user tries to upload more it should say "Maximum photos uploaded, delete to upload more". I'm not sure exactly how to pull this off.
photo.rb model:
class Photo < ActiveRecord::Base
attr_accessible :title, :body, :gallery_id, :name, :image, :remote_image_url
has_many :user, :through => :gallery
has_many :gallery
mount_uploader :image, ImageUploader
LIMIT = 5
validate do |record|
record.validate_photo_quota
end
def validate_photo_quota
return unless self.user
if self.gallery.user(:reload).count >= LIMIT
errors.add(:base, :exceeded_quota)
end
end
end
photo controller:
class PhotosController < ApplicationController
def new
#photo = Photo.new(:gallery_id => params[:gallery_id])
end
def create
#photo = Photo.new(params[:photo])
if #photo.save
flash[:notice] = "Successfully created photos."
redirect_to #photo.gallery
else
render :action => 'new'
end
end
def edit
#photo = Photo.find(params[:id])
end
def update
#photo = Photo.find(params[:id])
if #photo.update_attributes(paramas[:photo])
flash[:notice] = "Successfully updated photo."
redirect_to #photo.gallery
else
render :action => 'edit'
end
end
def destroy
#photo = Photo.find(params[:id])
#photo.destroy
flash[:notice] = "Successfully destroyed photo."
redirect_to #photo.gallery
end
end
galleries controller:
class GalleriesController < ApplicationController
def index
#galleries = Gallery.all
end
def show
#gallery = Gallery.find(params[:id])
end
def new
#gallery = Gallery.new
end
def create
#gallery = Gallery.new(params[:gallery])
if #gallery.save
flash[:notice] = "Successfully created gallery."
redirect_to #gallery
else
render :action => 'new'
end
end
def edit
#gallery = Gallery.find(params[:id])
end
def update
#gallery = Gallery.find(params[:id])
if #gallery.update_attributes(params[:gallery])
flash[:notice] = "Successfully updated gallery."
redirect_to gallery_url
else
render :action => 'edit'
end
end
def destroy
#gallery = Gallery.find(params[:id])
#gallery.destroy
flash[:notice] = "Successfully destroy gallery."
redirect_to galleries_url
end
end
Restricting user access
To restrict user access to certain models I would use something like CanCan.
It would let you do stuff like this:
## ability.rb
# Allow user to CRUD pictures belonging to own gallery
can :manage, Picture, gallery: {user: user}
In the controller you can then do stuff like this:
# picture_controller.rb
# assuming a nested route, e.g. galleries/1/pictures
load_and_authorize_resource :gallery
load_and_authorize_resource :picture, through: :gallery
This will make sure that each user only sees his or her own pictures.
Restricting number of pictures in gallery
I think your approach with the validation is okay.
I would simplify it thus:
validate :quota
private
def quota
return unless user
if gallery.pictures.count > 4
errors[:base] << "Maximum photos uploaded, delete to upload more"
end
end
The error message should probably go into a locale file.
Creating Gallery automatically for each user
To do this, make sure that the Gallery model has a belong_to association to User. Then create the gallery in a callback in the User model:
# models/user.rb
after_create :setup_gallery
private
def setup_gallery
Gallery.create(user: self)
end
General notes
When you define your has_many relations, you should use plural names, like has_many :users or has_many :galleries.

photo upload limit on carrier wave undefined method for user

I am using CarrierWave as my photo albums, and I am trying to setup so I can prevent users from only being able to upload maximum 5 photos to their gallery. However I am getting back a "undefined method `user'" error when clicking on the Upload Photo button with page title "NoMethodError in PhotosController#create"
Photo.rb:
class Photo < ActiveRecord::Base
attr_accessible :title, :body, :gallery_id, :name, :image, :remote_image_url
belongs_to :gallery
mount_uploader :image, ImageUploader
LIMIT = 5
validate do |record|
record.validate_photo_quota
end
def validate_photo_quota
return unless self.user
if self.user.photos(:reload).count >= LIMIT
errors.add(:base, :exceeded_quota)
end
end
end
Photos_controller:
class PhotosController < ApplicationController
def new
#photo = Photo.new(:gallery_id => params[:gallery_id])
end
def create
#photo = Photo.new(params[:photo])
if #photo.save
flash[:notice] = "Successfully created photos."
redirect_to #photo.gallery
else
render :action => 'new'
end
end
def edit
#photo = Photo.find(params[:id])
end
def update
#photo = Photo.find(params[:id])
if #photo.update_attributes(paramas[:photo])
flash[:notice] = "Successfully updated photo."
redirect_to #photo.gallery
else
render :action => 'edit'
end
end
def destroy
#photo = Photo.find(params[:id])
#photo.destroy
flash[:notice] = "Successfully destroyed photo."
redirect_to #photo.gallery
end
end
I thought I previously had user defined unless it has to be done for every controller?
You're calling self.user into the Photo model. The keyword self in that case represent an instance of photo. By your definition, a photo belongs to a gallery and therefore, there is no user to be called from photo.
If a gallery belongs to a user, then you should be able to call self.gallery.user to select the user owner of that photo.
You can also define a has_many :through association so as you can directly call the user from that photo, or retrive all the photos from that user.
This can be done following the documentation. In your case:
class User < ActiveRecord::Base
has_many :galeries
has_many :photos, :through => :galeries
end
class Photo < ActiveRecord::Base
belongs_to :user, :through => :gallery
belongs_to :gallery
end
class Gallery < ActiveRecord::Base
belongs_to :user
has_many :photos
end
Then you should be able to call photo.user and get the owner of the picture.

Rails 3: User has_many association id?

I have 3 models in my Rails 3 app:
User
model:
has_many :videos, :dependent => :destroy
controller:
before_filter :signed_in_user
def show
#user = User.find(params[:id])
end
def signed_in_user
unless signed_in?
store_location
redirect_to signin_path, notice: "Please sign in."
end
end
Video
model:
belongs_to :user
has_many :surveys, :dependent => :destroy
controller:
before_filter :signed_in_user
def signed_in_user
unless signed_in?
store_location
redirect_to signin_path, notice: "Please sign in."
end
end
def show
#video = Video.find(params[:id])
#original_video = #video.panda_video
#h264_encoding = #original_video.encodings["h264"]
#surveys = Survey.all
#user = User.find(params[:id])
end
Survey
model:
belongs_to :video
controller:
before_filter :signed_in_user
def signed_in_user
unless signed_in?
store_location
redirect_to signin_path, notice: "Please sign in."
end
end
def show
#survey = Survey.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.json { render json: #survey }
end
end
So in my app, a User has many Videos, and each Video has many Surveys (a.k.a. Reviews). Think of how Rotten Tomatoes works. A user has be signed in to either access a video, or write a review. Any review that user submits, while signed in, is automatically associated with that user...and this is what I'm trying to figure out with my app.
How do I associate the user id with the review? Right now, when a user is signed in, his name is automatically associated with all the reviews, whether he wrote them or not.
You want to use your Video class as a join model via the through option:
User
has_many :surveys, :through => :videos
Survey
has_one :user, :through => :video
This should let you do:
#user.surveys
#survey.user
Is there some reason this isn't as simple as:
User
has_many :surveys
Survey
belongs_to :user
If so, more info/code is required for a useful answer.

Paperclip: Stay put on edit

When a user edits something in my application, they're forced to re-upload their image via paperclip even if they aren't changing it. Failing to do so will cause an error, since I validate_presence_of :image. This is quite annoying.
How can I make it so Paperclip won't update its attributes if a user simply doesn't supply a new image on an edit?
The photo controller is fresh out of Rails' scaffold generator. The rest of the source code is provided below.
models/accommodation.rb
class Accommodation < ActiveRecord::Base
attr_accessible :photo
validates_presence_of :photo
has_one :photo
has_many :notifications
belongs_to :user
accepts_nested_attributes_for :photo, :allow_destroy => true
end
controllers/accommodation_controller.rb
class AccommodationsController < ApplicationController
def index
#accommodations = Accommodation.all
end
def show
#accommodation = Accommodation.find(params[:id])
rescue ActiveRecord::RecordNotFound
flash[:error] = "Accommodation not found."
redirect_to :home
end
def new
#accommodation = current_user.accommodations.build
#accommodation.build_photo
end
def create
#accommodation = current_user.accommodations.build(params[:accommodation])
if #accommodation.save
flash[:notice] = "Successfully created your accommodation."
redirect_to #accommodation
else
#accommodation.build_photo
render :new
end
end
def edit
#accommodation = Accommodation.find(params[:id])
#accommodation.build_photo
rescue ActiveRecord::RecordNotFound
flash[:error] = "Accommodation not found."
redirect_to :home
end
def update
#accommodation = Accommodation.find(params[:id])
if #accommodation.update_attributes(params[:accommodation])
flash[:notice] = "Successfully updated accommodation."
redirect_to #accommodation
else
#accommodation.build_photo
render :edit
end
end
def destroy
#accommodation = Accommodation.find(params[:id])
#accommodation.destroy
flash[:notice] = "Successfully destroyed accommodation."
redirect_to :inkeep
end
end
models/photo.rb
class Photo < ActiveRecord::Base
attr_accessible :image, :primary
belongs_to :accommodation
has_attached_file :image,
:styles => {
:thumb=> "100x100#",
:small => "150x150>" }
end
You shouldn't need #accommodation.build_photo anywhere else than in new action.

Resources