How to use private submit to hide from profile?

2019-01-27 06:45发布

问题:

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?

回答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!