Noosfero/noosfero

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

Summary

Maintainability
C
1 day
Test Coverage
require "diffy"

class ContentViewerController < ApplicationController
  needs_profile

  helper ProfileHelper
  helper TagsHelper

  def view_page
    path = get_path(params[:page], params[:format])

    @version = params[:version].to_i
    @npage = params[:npage] || "1"

    if path.blank?
      @page = profile.home_page
      return if redirected_to_profile_index
    else
      @page = profile.articles.find_by path: path
      return if redirected_page_from_old_path(path)
    end

    return unless allow_access_to_page(path)

    if @version > 0
      return render_access_denied unless @page.display_versions?
      return if rendered_versioned_article
    end

    redirect_to_translation && (return) if @page.profile.redirect_l10n

    if request.post? && @page.forum?
      process_forum_terms_of_use(user, params[:terms_accepted])
    elsif is_a_forum_topic?(@page) && !@page.parent.agrees_with_terms?(user)
      redirect_to @page.parent.url
      return
    end

    unless user_is_a_bot? || already_visited?(@page)
      Noosfero::Scheduler::Defer.later { @page.hit }
    end

    @page = FilePresenter.for @page

    return if rendered_file_download(params[:view])

    @form_div = params[:form]

    # FIXME see a better way to do this. It's not need to pass this variable anymore
    @comment = Comment.new

    process_page_posts(params)

    if @page.folder? && @page.gallery?
      @images = get_images(@page, params[:npage], params[:slideshow])
    end

    process_page_followers(params)

    process_comments(params)

    if request.xhr? && params[:comment_order]
      return render partial: "comment/comment", collection: @comments
    end

    if params[:slideshow]
      render action: "slideshow", layout: "slideshow"
      return
    end
    render :view_page, formats: [:html]
  end

  def versions_diff
    path = params[:page]
    @page = profile.articles.find_by path: path
    @v1, @v2 = @page.versions.find_by(version: params[:v1]), @page.versions.find_by(version: params[:v2])
  end

  def article_versions
    path = params[:page]
    @page = profile.articles.find_by path: path
    return unless allow_access_to_page(path)

    render_access_denied unless @page.display_versions?
    @versions = @page.versions.paginate(per_page: per_page, page: params[:npage])
  end

  def view_more_comments
    @page = Article.find_by_id params[:id]
    process_comments(params)
  end

  protected

    def per_page
      12
    end

    def redirect_to_translation
      locale = FastGettext.locale
      if !@page.language.nil? && @page.language != locale
        translations = [@page.native_translation] + @page.native_translation.translations
        urls = translations.map { |t| URI.parse(url_for(t.url)).path }
        urls << URI.parse(url_for(profile.admin_url.merge(controller: "cms", action: "edit", id: @page.id))).path
        urls << URI.parse(url_for(profile.admin_url.merge(controller: "cms", action: "new"))).path
        referer = URI.parse(url_for(request.referer)).path unless request.referer.blank?
        unless urls.include?(referer)
          translations.each do |translation|
            if translation.language == locale
              @page = translation
              redirect_to profile: @page.profile.identifier, page: @page.explode_path
              return true
            end
          end
        end
      end
      false
    end

    def allow_access_to_page(path)
      if @page.nil? # page not found, give error
        render_not_found(path)
        return false
      end

      unless @page.display_to?(user)
        render_access_denied
        return false
      end

      return true
    end

    def user_is_a_bot?
      user_agent = request.env["HTTP_USER_AGENT"]
      user_agent.blank? ||
        user_agent.match(/bot/) ||
        user_agent.match(/spider/) ||
        user_agent.match(/crawler/) ||
        user_agent.match(/\(.*https?:\/\/.*\)/)
    end

    def get_path(page, format = nil)
      path = page
      path = path.join("/") if path.kind_of?(Array)
      path = "#{path}.#{format}" if format

      return path
    end

    def redirected_to_profile_index
      if @page.nil?
        redirect_to controller: "profile", action: "index", profile: profile.identifier
        return true
      end

      return false
    end

    def redirected_page_from_old_path(path)
      unless @page
        page_from_old_path = profile.articles.find_by_old_path path
        if page_from_old_path
          redirect_to profile.url.merge(page: page_from_old_path.explode_path)
          return true
        end
      end

      return false
    end

    def process_forum_terms_of_use(user, terms_accepted = nil)
      if @page.forum? && @page.has_terms_of_use && terms_accepted == "true"
        @page.add_agreed_user(user)
      end
    end

    def is_a_forum_topic?(page)
      return (!@page.parent.nil? && @page.parent.forum?)
    end

    def rendered_versioned_article
      @versioned_article = @page.versions.find_by version: @version
      if @versioned_article && @page.versions.latest.version != @versioned_article.version
        render template: "content_viewer/versioned_article.html.erb"
        return true
      end

      return false
    end

    def rendered_file_download(view = nil)
      if @page.download? view
        data = @page.data

        # TODO test the condition
        if data.nil?
          raise "No data for file"
        end

        ## TODO: We will need to see how fix this using the new published schema.
        if @page.published && @page.uploaded_file?
          redirect_to "#{Noosfero.root}#{@page.public_filename}"
        else
          send_data data, @page.download_headers
        end

        return true
      end

      return false
    end

    def process_page_posts(params)
      if @page.has_posts?
        posts = get_posts(params[:year], params[:month])

        # FIXME Need to run this before the pagination because this version of
        #      will_paginate returns a will_paginate collection instead of a
        #      relation.
        posts = posts.native_translations if blog_with_translation?(@page)

        # Here we filter the accessible posts
        @posts = posts.accessible_to(user).paginate(page: params[:npage], per_page: @page.posts_per_page).to_a
        if blog_with_translation?(@page)
          @posts.replace @posts.map { |p| p.get_translation_to(FastGettext.locale) }.compact
        end
      end
    end

    def get_posts(year = nil, month = nil)
      if year && month
        begin
          filter_date = DateTime.parse("#{year}-#{month}-01")
          return @page.posts.by_range(filter_date..filter_date.at_end_of_month)
        rescue ArgumentError
          return @page.posts
        end
      else
        return @page.posts
      end
    end

    def blog_with_translation?(page)
      return (page.blog? && page.display_posts_in_current_language?)
    end

    def get_images(page, npage, slideshow)
      images = page.images.select { |a| a.display_to? user }
      images = images.paginate(per_page: per_page, page: npage) unless slideshow

      return images
    end

    def process_page_followers(params)
      @unfollow_form = params[:unfollow] == "true"
      if params[:unfollow] == "commit" && request.post?
        @page.followers -= [params[:email]]
        if @page.save
          session[:notice] = _("Notification of new comments to '%s' was successfully canceled") % params[:email]
        end
      end
    end

    def process_comments(params)
      @comments = @page.comments.without_spam
      @comments = @plugins.filter(:unavailable_comments, @comments)
      @comments_count = @comments.count
      @comment_order = params[:comment_order].nil? ? "newest" : params[:comment_order]
      @comments = @comments.without_reply
      if @comment_order == "newest"
        @comments = @comments.reverse
      end
      @curr_page = (params[:comment_page] || 1).to_i
      @total_pages = (@comments.size.to_f / per_page).ceil
      @comments = @comments.paginate(per_page: per_page, page: params[:comment_page])
    end

  private

    def already_visited?(element)
      user_id = if user.nil? then -1 else current_user.id end
      user_id = "#{user_id}_#{element.id}_#{element.class}"

      if cookies.signed[:visited] == user_id
        return true
      else
        cookies.permanent.signed[:visited] = user_id
        return false
      end
    end
end