Noosfero/noosfero

View on GitHub
app/controllers/public/profile_controller.rb

Summary

Maintainability
D
2 days
Test Coverage
class ProfileController < PublicController
  needs_profile
  before_action :check_access_to_profile, except: [:join, :join_not_logged, :index, :add]
  before_action :store_location, only: [:join, :join_not_logged, :report_abuse, :send_mail]
  before_action :login_required, only: [:add, :join, :leave, :unblock, :leave_scrap, :remove_scrap, :remove_activity, :view_more_activities, :view_more_network_activities, :report_abuse, :register_report, :leave_comment_on_activity, :send_mail, :follow, :unfollow]
  before_action :allow_followers?, only: [:follow, :unfollow]
  before_action :accept_only_post, only: [:follow, :unfollow]
  before_action :allow_scrap?, only: [:leave_scrap]
  before_action :allow_comment?, only: [:leave_comment_on_activity]
  before_action :load_tags, only: [:index, :about]

  include ProfileHelper
  helper TagsHelper
  helper ActionTrackerHelper
  helper CustomFieldsHelper

  include Captcha

  protect "send_mail_to_members", :profile, only: [:send_mail]

  ACTIVITIES_PER_PAGE = 15

  def index
    @offsets = { wall: 0, network: 0 }
    page = (params[:page] || 1).to_i
    if logged_in?
      @activities = loop_fetch_activities(@profile.activities, :wall, page) if @profile.display_to?(user, :wall)
      @network_activities = loop_fetch_activities(@profile.tracked_notifications, :network, page) if @profile == user
    end
    allow_access_to_page
  end

  def about
  end

  def activities
    @offsets = { wall: 0, network: 0 }
    page = (params[:page] || 1).to_i
    @activities = loop_fetch_activities(@profile.activities, :wall, page) if @profile.display_to?(user, :wall)
  end

  def tags
    @tags_cache_key = "tags_profile_#{profile.id.to_s}"
    if is_cache_expired?(@tags_cache_key)
      @tags = profile.article_tags
    end
  end

  def content_tagged
    @tag = params[:id]
    @tag_cache_key = "tag_#{CGI.escape(@tag.to_s)}_#{profile.id.to_s}_page_#{params[:npage]}"
    if is_cache_expired?(@tag_cache_key)
      @tagged = profile.articles.tagged_with(@tag).paginate(per_page: 20, page: params[:npage])
    end
  end

  def tag_feed
    @tag = params[:id]
    tagged = profile.articles.paginate(per_page: 20, page: 1).order("published_at DESC").joins(:tags).where("tags.name LIKE ?", @tag)
    feed_writer = FeedWriter.new
    data = feed_writer.write(
      tagged,
      title: _("%s's contents tagged with \"%s\"").html_safe % [profile.name, @tag],
      description: _("%s's contents tagged with \"%s\"").html_safe % [profile.name, @tag],
      link: url_for(profile.url)
    )
    render plain: data, content_type: "text/xml"
  end

  def communities
    if is_cache_expired?(profile.communities_cache_key(params))
      @communities = profile.communities.includes(relations_to_include).paginate(per_page: per_page, page: params[:npage], total_entries: profile.communities.count)
    end
  end

  def enterprises
    @enterprises = profile.enterprises.includes(relations_to_include)
  end

  def friends
    if is_cache_expired?(profile.friends_cache_key(params))
      @friends = profile.friends.order(:name).includes(relations_to_include)
                        .paginate(per_page: per_page, page: params[:npage],
                                  total_entries: profile.friends.count)
    end
  end

  def following
    @followed_people = profile.followed_profiles.paginate(per_page: per_page, page: params[:npage], total_entries: profile.followed_profiles.count)
  end

  def followed
    @followed_by = profile.followers.paginate(per_page: per_page, page: params[:npage], total_entries: profile.followers.count)
  end

  def members
    if is_cache_expired?(profile.members_cache_key(params))
      sort = (params[:sort] == "desc") ? params[:sort] : "asc"
      @profile_admins = profile.admins.includes(relations_to_include).order("name #{sort}").paginate(per_page: members_per_page, page: params[:npage])
      @profile_members = profile.members.includes(relations_to_include).order("name #{sort}").paginate(per_page: members_per_page, page: params[:npage])
      @profile_members_url = url_for(controller: "profile", action: "members")
    end
  end

  def fans
    @fans = profile.fans.includes(relations_to_include)
  end

  def favorite_enterprises
    @favorite_enterprises = profile.favorite_enterprises.includes(relations_to_include)
  end

  def sitemap
    @articles = profile.top_level_articles.includes([:profile, :parent])
  end

  def join_modal
    profile.add_member(user)
    session[:notice] = _("%s administrator still needs to accept you as member.").html_safe % profile.name
    redirect_to action: :index
  end

  def join
    if !user.memberships.include?(profile)
      return if profile.community? && show_confirmation_modal?(profile)

      profile.add_member(user)
      if !profile.members.include?(user)
        render plain: { message: _("%s administrator still needs to accept you as member.").html_safe % profile.name }.to_json
      else
        render plain: { message: _("You just became a member of %s.").html_safe % profile.name }.to_json
      end
    else
      render plain: { message: _("You are already a member of %s.").html_safe % profile.name }.to_json
    end
  end

  def join_not_logged
    session[:join] = profile.identifier

    if user
      redirect_to controller: "profile", action: "join"
    else
      redirect_to controller: "/account", action: "login", return_to: profile.url
    end
  end

  def leave
    if current_person.memberships.include?(profile)
      if current_person.is_last_admin?(profile)
        render plain: { redirect_to: url_for(controller: "profile_members", action: "last_admin", person: current_person.id) }.to_json
      else
        render plain: current_person.leave(profile, params[:reload])
      end
    else
      render plain: { message: _("You are not a member of %s.").html_safe % profile.name }.to_json
    end
  end

  def check_membership
    unless logged_in?
      render plain: ""
      return
    end
    if user.memberships.include?(profile)
      render plain: "true"
    else
      render plain: "false"
    end
  end

  def add
    # FIXME this shouldn't be in Person model?
    if !user.memberships.include?(profile)
      AddFriend.create!(person: user, friend: profile)
      render plain: _("%s still needs to accept being your friend.").html_safe % profile.name
    else
      render plain: _("You are already a friend of %s.").html_safe % profile.name
    end
  end

  def follow
    if profile.followed_by?(current_person)
      render plain: _("You are already following %s.") % profile.name, status: 200
    else
      selected_circles = params[:circles].to_h.map { |circle_name, circle_id| Circle.find_by(id: circle_id) }.select { |c| c.present? }
      if selected_circles.present?
        current_person.follow(profile, selected_circles)
        render plain: _("You are now following %s") % profile.name, status: 200
      else
        render plain: _("Select at least one circle to follow %s.") % profile.name, status: 400
      end
    end
  end

  def find_profile_circles
    circles = Circle.where(person: current_person, profile_type: profile.class.name)
    render partial: "blocks/profile_info_actions/circles", locals: { circles: circles, profile_types: Circle.profile_types.to_a }
  end

  def unfollow
    follower = params[:follower_id].present? ? Person.find_by(id: params[:follower_id]) : current_person

    if follower && follower.follows?(profile)
      follower.unfollow(profile)
    end
    redirect_url = params["redirect_to"] ? params["redirect_to"] : profile.url
    redirect_to redirect_url
  end

  def check_friendship
    unless logged_in?
      render plain: ""
      return
    end
    if user == profile || user.already_request_friendship?(profile) || user.is_a_friend?(profile)
      render plain: "true"
    else
      render plain: "false"
    end
  end

  def follow_article
    article = profile.environment.articles.find params[:article_id]
    article.person_followers << user
    redirect_to article.url
  end

  def unfollow_article
    article = profile.environment.articles.find params[:article_id]
    article.person_followers.delete(user)
    redirect_to article.url
  end

  def unblock
    if current_user.person.is_admin?(profile.environment)
      profile.unblock
      session[:notice] = _("You have unblocked %s successfully. ").html_safe % profile.name
      redirect_to controller: "profile", action: "index"
    else
      message = _("You are not allowed to unblock enterprises in this environment.")
      render_access_denied(message)
    end
  end

  def leave_scrap
    sender = params[:sender_id].nil? ? current_user.person : Person.find(params[:sender_id])
    receiver = params[:receiver_id].nil? ? @profile : Person.find(params[:receiver_id])
    @scrap = Scrap.new(params[:scrap])
    @scrap.sender = sender
    @scrap.receiver = receiver
    @scrap.marked_people = treat_followed_entries(params[:filter_followed])
    @tab_action = params[:tab_action]
    @message = @scrap.save ? _("Message successfully sent.") : _("You can't leave an empty message.")
    activities = @profile.activities.paginate(per_page: 15, page: params[:page]) if params[:not_load_scraps].nil?
    render partial: "profile_activities_list", locals: { activities: activities }
  end

  def leave_comment_on_activity
    @comment = Comment.new(params[:comment])
    @comment.author = user
    @activity = ActionTracker::Record.find(params[:source_id])
    @comment.source_type, @comment.source_id = (@activity.target_type == "Article" ? ["Article", @activity.target_id] : [@activity.class.to_s, @activity.id])
    @tab_action = params[:tab_action]
    @message = @comment.save ? _("Comment successfully added.") : _("You can't leave an empty comment.")
    if @tab_action == "wall"
      activities = @profile.activities.paginate(per_page: 15, page: params[:page]) if params[:not_load_scraps].nil?
      render partial: "profile_activities_list", locals: { activities: activities }
    else
      network_activities = @profile.tracked_notifications.visible.paginate(per_page: 15, page: params[:page])
      render partial: "profile_network_activities", locals: { activities: network_activities }
    end
  end

  def search_followed
    result = []
    circles = find_by_contents(:circles, user, user.circles.where(profile_type: "Person"), params[:q])[:results]
    followed = find_by_contents(:followed, user, Profile.followed_by(user), params[:q])[:results]
    result = circles + followed
    render plain: prepare_to_token_input_by_class(result).to_json
  end

  def loop_fetch_activities(base_activities, kind, page)
    activities = nil
    while activities.nil? || (activities.empty? && page <= activities.total_pages)
      activities = base_activities.offset(@offsets[kind.to_sym]).paginate(per_page: ACTIVITIES_PER_PAGE, page: page)
      activities = filter_activities(activities, kind.to_sym)
      page += 1
    end
    activities
  end

  def view_more_activities
    @activities = nil
    @offsets = params[:offsets]
    page = (params[:page] || 1).to_i
    kind = params[:kind]

    if kind == "wall"
      base_activities = @profile.activities
      partial = "profile_activities_list"
    else
      base_activities = @profile.tracked_notifications
      partial = "profile_network_activities"
    end

    @activities = loop_fetch_activities(base_activities, kind, page)
    render partial: partial, locals: { activities: @activities }
  end

  def more_comments
    profile_filter = @profile.person? ? { user_id: @profile } : { target_id: @profile }
    @activity = ActionTracker::Record.where(id: params[:activity])
    @activity = @activity.where(profile_filter) if !logged_in? || !current_person.follows?(@profile)
    @activity = @activity.first

    comments_count = @activity.comments.count
    @comment_page = (params[:comment_page] || 1).to_i
    @comments_per_page = 5
    @no_more_pages = comments_count <= @comment_page * @comments_per_page
  end

  def more_replies
    @activity = Scrap.where(id: params[:activity], receiver_id: @profile, scrap_id: nil).first
    @comments_count = @activity.replies.count
    @comment_page = (params[:comment_page] || 1).to_i
    @comments_per_page = 5
    @no_more_pages = @comments_count <= @comment_page * @comments_per_page
  end

  def remove_scrap
    begin
      scrap = current_user.person.scraps(params[:scrap_id])
      scrap.destroy
      finish_successful_removal "Scrap successfully removed."
    rescue
      finish_unsuccessful_removal "You could not remove this scrap."
    end
  end

  def remove_activity
    begin
      raise if !can_edit_profile

      activity = ActionTracker::Record.find(params[:activity_id])
      if params[:only_hide]
        activity.update_attribute(:visible, false)
      else
        activity.destroy
      end
      finish_successful_removal "Activity successfully removed."
    rescue
      finish_unsuccessful_removal "You could not remove this activity."
    end
  end

  def remove_notification
    begin
      raise if !can_edit_profile

      notification = ActionTrackerNotification.where(profile_id: profile.id, action_tracker_id: params[:activity_id]).first
      notification.destroy
      render plain: _("Notification successfully removed.")
    rescue
      render plain: _("You could not remove this notification.")
    end
  end

  def finish_successful_removal(msg)
    if request.xhr?
      render plain: { "ok" => true }.to_json, content_type: "application/json"
    else
      session[:notice] = _(msg)
      redirect_to action: :index
    end
  end

  def finish_unsuccessful_removal(msg)
    session[:notice] = _(msg)
    if request.xhr?
      render plain: { "redirect" => url_for(action: :index) }.to_json, content_type: "application/json"
    else
      redirect_to action: :index
    end
  end

  def report_abuse
    @abuse_report = AbuseReport.new
    render layout: false
  end

  def register_report
    unless verify_captcha(:report_abuse, nil, user, environment, profile)
      render plain: {
        ok: false,
        error: {
          code: 1,
          message: _("You could not answer the captcha.")
        }
      }.to_json
    else
      begin
        abuse_report = AbuseReport.new(params[:abuse_report])
        if !params[:content_type].blank?
          article = params[:content_type].constantize.find(params[:content_id])
          abuse_report.content = article_reported_version(article)
        end

        user.register_report(abuse_report, profile)

        if !params[:content_type].blank?
          abuse_report = AbuseReport.find_by(reporter_id: user.id, abuse_complaint_id: profile.opened_abuse_complaint.id)
          Delayed::Job.enqueue DownloadReportedImagesJob.new(abuse_report, article)
        end

        render plain: {
          ok: true,
          message: _("Your abuse report was registered. The administrators are reviewing your report."),
        }.to_json
      rescue Exception => exception
        logger.error(exception.to_s)
        render plain: {
          ok: false,
          error: {
            code: 2,
            message: _("Your report couldn't be saved due to some problem. Please contact the administrator.")
          }
        }.to_json
      end
    end
  end

  def remove_comment
    # FIXME Check whether these permissions are enough
    @comment = Comment.find(params[:comment_id])
    if (user == @comment.author || user == profile || user.has_permission?(:moderate_comments, profile))
      @comment.destroy
      finish_successful_removal "Comment successfully removed."
    else
      finish_unsuccessful_removal "You could not remove this comment."
    end
  end

  def send_mail
    @mailing = profile.mailings.build(params[:mailing])
    @mailing.data = session[:members_filtered] ? { members_filtered: session[:members_filtered] } : {}
    @email_templates = profile.email_templates.where template_type: :organization_members
    if request.post?
      @mailing.locale = locale
      @mailing.person = user
      if @mailing.save
        session[:notice] = _("The e-mails are being sent")
        redirect_to_previous_location
      else
        session[:notice] = _("Could not create the e-mail")
      end
    end
  end

  protected

    def check_access_to_profile
      unless profile.display_to?(user)
        redirect_to action: "index"
      end
    end

    def store_location
      if session[:previous_location].nil?
        session[:previous_location] = request.referer
      end
    end

    def redirect_to_previous_location
      back = session[:previous_location]
      if back
        session[:previous_location] = nil
        redirect_to back
      else
        redirect_to profile.url
      end
    end

    def per_page
      Noosfero::Constants::PROFILE_PER_PAGE
    end

    def members_per_page
      20
    end

    def can_edit_profile
      @can_edit_profile ||= user && user.has_permission?("edit_profile", profile)
    end
    helper_method :can_edit_profile

    def relations_to_include
      [:image, :domains, :preferred_domain, :environment]
    end

    def allow_followers?
      render_not_found unless profile.allow_followers?
    end

    def treat_followed_entries(entries)
      return [] if entries.blank? || profile != user

      followed = []
      entries.split(",").map do |entry|
        klass, identifier = entry.split("_")
        case klass
        when "Person"
          followed << Person.find(identifier)
        when "Circle"
          circle = Circle.find(identifier)
          followed += Profile.in_circle(circle)
        end
      end
      followed.uniq
    end

    def filter_activities(activities, kind)
      @offsets ||= { wall: 0, network: 0 }
      return activities if environment.admins.include?(user)

      activities = activities.to_a
      initial_count = activities.count
      activities.delete_if do |activity|
        activity = ActivityPresenter.for(activity)
        next if activity.involved?(user)

        activity.hidden_for?(user)
      end
      @offsets[kind] = @offsets[kind].to_i
      @offsets[kind] += initial_count - activities.count
      activities
    end

    def follow_profile?
      logged_in? && current_person.follows?(@profile)
    end

    def allow_scrap?
      logged_in? && (current_person == @profile || (current_person.is_member_of?(@profile) || current_person.is_a_friend?(@profile)))
    end
    helper_method :allow_scrap?

    def allow_comment?
      logged_in?
    end
    helper_method :allow_comment?

    def load_tags
      @tags = profile.article_tags
    end
end