1

When a User submits via private how can we hide the submitted info from the feed and from other users being able to see it on his public profile?

<%= button_tag(type: 'submit', class: "btn")  do %>
  ...
<%= button_tag(type: 'submit', class: "btn", id: "2", name: 'private')  do %>
  ...

We put the below in the controller, but since the private button will be in a lot of different _forms, do I have to put it in each controller or can we put it in the application controller?

if params[:private]
  # the private action / What do we need to put here?
else
  # normal submit / and here?

I followed this railcast episode nearly to the T to build the activity feed: http://railscasts.com/episodes/406-public-activity.

Here 's the code for the public profile:

users_controller.rb

def show
   @user = User.find(params[:id])
   @habits = @user.habits
   @valuations = @user.valuations
   @accomplished_goals = @user.goals.accomplished
   @unaccomplished_goals = @user.goals.unaccomplished
   @averaged_quantifieds = @user.quantifieds.averaged
   @instance_quantifieds = @user.quantifieds.instance
end

show.html.erb

<% if @user.habits.any? %>
  <h2>Habits</h2>
  <h4>Challenges</h4>
  <%= render partial: 'habits', locals: {habits: @habits} %>
<% end %>

<% if @user.valuations.any? %>
  <h2>Values</h2>
  <%= render @valuations %>
<% end %>

<% if @user.goals.any? %>
  <h2>Goals</h2>
  <h4> Current</h4>
  <%= render @unaccomplished_goals %>
<% end %>

<% if @user.goals.any? %>
  <h4>Accomplished</h4>
  <%= render @accomplished_goals %>
<% end %>

<% if @user.quantifieds.any? %>
  <h2>Stats</h2>
  <h4>Averaged</h4>
  <%= render partial: 'averaged', locals: {habits: @averaged_quantifieds} %>
<% end %>

<% if @user.quantifieds.any? %>
  <h4>Instance</h4>
  <%= render partial: 'instance', locals: {habits: @instance_quantifieds} %>
<% end %>

As Requested :)

User Model

class User < ActiveRecord::Base
  has_many :authentications
  has_many :habits, dependent: :destroy
  has_many :levels
  has_many :valuations, dependent: :destroy
  has_many :comments, as: :commentable
  has_many :goals, dependent: :destroy
  has_many :quantifieds, dependent: :destroy
  has_many :results, through: :quantifieds
  accepts_nested_attributes_for :quantifieds, :reject_if => :all_blank, :allow_destroy => true
  accepts_nested_attributes_for :results, :reject_if => :all_blank, :allow_destroy => true
  has_many :active_relationships, class_name:  "Relationship",
                                  foreign_key: "follower_id",
                                  dependent:   :destroy
  has_many :passive_relationships, class_name:  "Relationship",
                                   foreign_key: "followed_id",
                                   dependent:   :destroy
  has_many :following, through: :active_relationships,  source: :followed
  has_many :followers, through: :passive_relationships, source: :follower
  attr_accessor :remember_token, :activation_token, :reset_token
  before_save   :downcase_email
  before_create :create_activation_digest
  validates :name,  presence: true, length: { maximum: 50 }
  VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i
  validates :email, presence: true, length: { maximum: 255 },
                    format: { with: VALID_EMAIL_REGEX },
                    uniqueness: { case_sensitive: false }, unless: -> { from_omniauth? }
  has_secure_password
  validates :password, length: { minimum: 6 }


  def self.from_omniauth(auth)
    where(provider: auth.provider, uid: auth.uid).first_or_initialize.tap do |user|
      user.provider = auth.provider
      user.uid = auth.uid
      user.name = auth.info.name
      user.oauth_token = auth.credentials.token
      user.oauth_expires_at = Time.at(auth.credentials.expires_at)
      user.password = (0...8).map { (65 + rand(26)).chr }.join
      user.email = (0...8).map { (65 + rand(26)).chr }.join+"@mailinator.com"
      user.save!
    end
  end

  # Returns the hash digest of the given string.
  def User.digest(string)
    cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST :
                                                  BCrypt::Engine.cost
    BCrypt::Password.create(string, cost: cost)
  end

  # Returns a random token.
  def User.new_token
    SecureRandom.urlsafe_base64
  end

  # Remembers a user in the database for use in persistent sessions.
  def remember
    self.remember_token = User.new_token
    update_attribute(:remember_digest, User.digest(remember_token))
  end

  # Forgets a user. NOT SURE IF I REMOVE
  def forget
    update_attribute(:remember_digest, nil)
  end

  # Returns true if the given token matches the digest.
  def authenticated?(attribute, token)
    digest = send("#{attribute}_digest")
    return false if digest.nil?
    BCrypt::Password.new(digest).is_password?(token)
  end

  # Activates an account.
  def activate
    update_attribute(:activated,    true)
    update_attribute(:activated_at, Time.zone.now)
  end

  # Sends activation email.
  def send_activation_email
    UserMailer.account_activation(self).deliver_now
  end

  def create_reset_digest
    self.reset_token = User.new_token
    update_attribute(:reset_digest,  User.digest(reset_token))
    update_attribute(:reset_sent_at, Time.zone.now)
  end

  # Sends password reset email.
  def send_password_reset_email
    UserMailer.password_reset(self).deliver_now
  end

   # Returns true if a password reset has expired.
  def password_reset_expired?
    reset_sent_at < 2.hours.ago
  end

  def good_results_count
    results.good_count
  end

  # Returns status feed.
  def feed
    following_ids = "SELECT followed_id FROM relationships
                     WHERE  follower_id = :user_id"
    Habit.where("user_id IN (#{following_ids})
                     OR user_id = :user_id", user_id: id)
    Valuation.where("user_id IN (#{following_ids})
                     OR user_id = :user_id", user_id: id)
    Goal.where("user_id IN (#{following_ids})
                     OR user_id = :user_id", user_id: id)
    Quantified.where("user_id IN (#{following_ids})
                     OR user_id = :user_id", user_id: id)
  end

  # Follows a user.
  def follow(other_user)
    active_relationships.create(followed_id: other_user.id)
  end

  # Unfollows a user.
  def unfollow(other_user)
    active_relationships.find_by(followed_id: other_user.id).destroy
  end

  # Returns true if the current user is following the other user.
  def following?(other_user)
    following.include?(other_user)
  end

private 

    def from_omniauth? 
    provider && uid 
    end

      # Converts email to all lower-case.
    def downcase_email 
      self.email = email.downcase unless from_omniauth? 
    end

    # Creates and assigns the activation token and digest.
    def create_activation_digest
      self.activation_token  = User.new_token
      self.activation_digest = User.digest(activation_token)
    end
end

User Controller

class UsersController < ApplicationController
  before_action :logged_in_user, only: [:index, :edit, :update, :destroy,
                                        :following, :followers]
  before_action :correct_user,   only: [:edit, :update]
  before_action :admin_user,     only: :destroy

  def index
    @users = User.paginate(page: params[:page])
  end

  def show
    @user = User.find(params[:id])
    @habits = @user.habits
    @valuations = @user.valuations
    @accomplished_goals = @user.goals.accomplished
    @unaccomplished_goals = @user.goals.unaccomplished
    @averaged_quantifieds = @user.quantifieds.averaged
    @instance_quantifieds = @user.quantifieds.instance
  end

  def new
    @user = User.new
  end

  def create
    @user = User.new(user_params)
    if @user.save
      @user.send_activation_email
      flash[:info] = "Please check your email to activate your account."
      redirect_to root_url
    else
      @feed_items = []
      render 'pages/home'
    end
  end

  def edit
    @user = User.find(params[:id])
  end

  def update
    @user = User.find(params[:id])
    if @user.update_attributes(user_params)
      flash[:success] = "Profile updated"
      redirect_to @user
    else
      render 'edit'
    end
  end

  def destroy
    User.find(params[:id]).destroy
    flash[:success] = "User deleted"
    redirect_to users_url
  end

  def following
    @title = "Following"
    @user  = User.find(params[:id])
    @users = @user.following.paginate(page: params[:page])
    render 'show_follow'
  end

  def followers
    @title = "Followers"
    @user  = User.find(params[:id])
    @users = @user.followers.paginate(page: params[:page])
    render 'show_follow'
  end
  
  private

    def user_params
      params.require(:user).permit(:name, :email, :password,
                                   :password_confirmation)
    end

# Before filters

# Confirms a logged-in user.
    def logged_in_user
      unless logged_in?
        store_location
        flash[:danger] = "Please log in."
        redirect_to login_url
      end
    end

    # Confirms the correct user.
    def correct_user
      @user = User.find(params[:id])
      redirect_to(root_url) unless current_user?(@user)
    end

    # Confirms an admin user.
    def admin_user
      redirect_to(root_url) unless current_user.admin?
    end
end

UPDATE

With K's answer below I get this error message upon going to users or users/1, users/2 etc..

Started GET "/users/1" for 127.0.0.1 at 2015-04-01 16:32:13 -0400

SyntaxError (/Users/galli01anthony/Desktop/Pecoce/app/controllers/users_controller.rb:79: syntax error, unexpected ':', expecting keyword_end
      users_attributes: [:name, :email, :password, :...
                       ^
/Users/galli01anthony/Desktop/Pecoce/app/controllers/users_controller.rb:79: syntax error, unexpected ',', expecting keyword_end
...ivate, :password_confirmation], valuations_attributes: [:nam...
...                               ^
/Users/galli01anthony/Desktop/Pecoce/app/controllers/users_controller.rb:79: syntax error, unexpected '=', expecting keyword_end
... [:name, :tag_list, :private] = true
...                               ^
/Users/galli01anthony/Desktop/Pecoce/app/controllers/users_controller.rb:81: syntax error, unexpected ':', expecting keyword_end
      users_attributes: [:name, :email, :password, :...
                       ^
/Users/galli01anthony/Desktop/Pecoce/app/controllers/users_controller.rb:81: syntax error, unexpected ',', expecting keyword_end
...sword, :password_confirmation], valuations_attributes: [:nam...
...                               ^):
  app/controllers/users_controller.rb:79: syntax error, unexpected ':', expecting keyword_end
  app/controllers/users_controller.rb:79: syntax error, unexpected ',', expecting keyword_end
  app/controllers/users_controller.rb:79: syntax error, unexpected '=', expecting keyword_end
  app/controllers/users_controller.rb:81: syntax error, unexpected ':', expecting keyword_end
  app/controllers/users_controller.rb:81: syntax error, unexpected ',', expecting keyword_end

This is a two-part question. Find second part here: How to use private submit to hide from feed?

Community
  • 1
  • 1

1 Answers1

1

Add a field 'private' to the User model with its default value 'false'. All normal user informations will be flagged as 'public' (because the private field has the value false) Only if params[:private], then the value of the private field will be set to 'true'.

Next you can add a method to the user model which will grab only the data of user with the private = false flag (for public views).

EDIT:

Displaying public or private:

Add a field 'private' to each of your related models which possibly could be marked as private. Don't forget to add this in your migrations. Set the private's default to false.

Include in valuation & user migration/schema

t.boolean :private, default: false

valuation.rb

def public?
  private == true ? false : true
end

user.rb

# gets public valutations or nil, if there's no public valutation
def public_valuations
    valuations.find(&:public?)
end

Do this in the same way for each of your wanted relations. It enables you to get the public informations via

@valuations = @user.public_valuations

Your current show action displays now all additional user's informations - public and private - which are should be only displayed if the current_user = @user.

At last you have to insert a condition in your show action:

def show
  @user = User.find(params[:id])
  if current_user == @user
    @habits = @user.habits
    @valuations = @user.valuations
    @accomplished_goals = @user.goals.accomplished
    @unaccomplished_goals = @user.goals.unaccomplished
    @averaged_quantifieds = @user.quantifieds.averaged
    @instance_quantifieds = @user.quantifieds.instance
  else
    @valuations = @user.public_valuations
  end
end

That solution depends on current_user, i.e. you must have a method which returns the object of the currently logged_in user (maybe in a session). Michael Hartl wrote an awesome tutorial about user authentication. *RubyonRailsBeginner used Hartl Tutorial for this :)

Creating public or private records

Since you had set the private's default to false, you can use your existing code to create public entries.

For private entries you must set the corresponding attribute in your user_params to true.

EDIT with params.require:

I set the [:private] in the else clause explicit to false, so that a user might set his private attributes to public, if wanted.

def user_params
  if params[:private] = true
    params.require(:user).permit(:name, :email, :password, :private, :password_confirmation, valuations_attributes: [:name, :tag_list, :private])
  else
    params[:user][:valuations][:private] = false
    params.require(:user).permit(:name, :email, :password, :password_confirmation, valuations_attributes: [:name, :tag_list])
  end
end

The Rails Api gives you some hints about strong parameters with nested attributes.

Hope that helps!

Community
  • 1
  • 1
Klaus
  • 1,591
  • 1
  • 12
  • 18
  • 1
    Danke for your help, again! Okay so I added private to user in the schema. Then you lost me a little. Where do I add `params[:private]` Then I add a method to the user model What's in the method I'm confused by what you wrote there. Thanks though you helped clarify some things for me. –  Mar 29 '15 at 02:29
  • Would you please post your User model and UsersController (only the create and update action) in your OP? So I can make some suggestions referring to your existing code – Klaus Mar 29 '15 at 08:23
  • Okay, viewing the code con I think that I misunderstood you: You want to have the related has_many relations (level, goals etc.) signed as public or private, right? If so, do you have different forms for each of these? Or do you want to make them all private or all public? – Klaus Mar 30 '15 at 06:45
  • 1
    Yea Klausinho I have different forms for each one. Within each form are the `button_tags` I listed in the question. When a user clicks the `'private'` `button_tag` is where we'd need to create the logic so that the info is hidden from the feed/public profile. du verstehst? –  Mar 30 '15 at 16:23
  • I edited your answer to what I thought you might might mean :/ Superb answer overall though. Sorry for my incompetence. –  Mar 30 '15 at 22:05
  • Yes, that's almost correct. I was just too lazy to write down all the params ;) I made a little correction in the if-statement. – Klaus Mar 30 '15 at 22:07
  • Fantastic! I'll give it all a try now. –  Mar 30 '15 at 22:09
  • Hey Klausinho. I keep getting a long error when I go to http://0.0.0.0:3000/users. I updated the question with the error above. Thanks for going above and beeeyyond =) –  Apr 01 '15 at 20:10
  • Klausinho for this question let's just focus on getting valuations to work (I'll duplicate later) so I went into your question and put what I currently have with it. Maybe I made a stupid error there I'm just overlooking or potentially misunderstanding you. –  Apr 01 '15 at 20:22
  • If you had taken a look into the linked API, you may have noticed, that the params must be permitted. You do that with `params.require(:user).permit`. I've edited the answer to that. – Klaus Apr 02 '15 at 06:46
  • Yes we got it working! I updated your question again because you left a few things out. I read the API sorry but I kept getting errors when I had what you had, but I realized I needed to just tweak it more instead of reverting back. The question was a two parter. How to hide from public profile (success!) and the feed, but instead of continuing on with this question I’ll split it up because I want to be respectful of your time and to keep the questions well targeted. Thanks again! –  Apr 04 '15 at 02:41
  • If you still want to continue the conversation I can meet you over here: http://stackoverflow.com/questions/29442123/how-to-use-private-submit-to-hide-from-feed =] –  Apr 04 '15 at 02:41
  • 1
    `def public? !private end` much simplier – Avdept Apr 11 '15 at 17:08