TracksApp/tracks

View on GitHub
app/controllers/projects_controller.rb

Summary

Maintainability
A
4 hrs
Test Coverage
class ProjectsController < ApplicationController
  helper :application, :todos, :notes
  before_action :set_source_view
  before_action :set_project_from_params, :only => [:update, :destroy, :show, :edit, :set_reviewed]
  before_action :default_context_filter, :only => [:create, :update]
  skip_before_action :login_required, :only => [:index, :show]
  prepend_before_action :login_or_feed_token_required, :only => [:index, :show]

  def index
    @source_view = params['_source_view'] || 'project_list'
    if params[:projects_and_actions]
      projects_and_actions
    else
      @contexts = current_user.contexts
      init_not_done_counts(['project'])
      init_hidden_todo_counts(['project'])
      if params[:only_active_with_no_next_actions]
        @projects = current_user.projects.active.select { |p| count_undone_todos(p) == 0 }
      else
        @projects = current_user.projects
      end
      @active_projects = current_user.projects.active
      @hidden_projects = current_user.projects.hidden
      respond_to do |format|
        format.html do
          @page_title = t('projects.list_projects')
          @count = current_user.projects.count
          @completed_projects = current_user.projects.completed.limit(10)
          @completed_count = current_user.projects.completed.count
          @no_projects = current_user.projects.empty?
          current_user.projects.cache_note_counts
          @new_project = current_user.projects.build
        end
        format.m do
          @completed_projects = current_user.projects.completed
          @down_count = @active_projects.size + @hidden_projects.size + @completed_projects.size
          cookies[:mobile_url] = { :value => request.fullpath, :secure => SITE_CONFIG['secure_cookies'] }
        end
        format.xml { render :xml => @projects.to_xml(:root => :projects, :except => :user_id) }
        format.any(:rss, :atom) do
          @feed_title = I18n.t('models.project.feed_title')
          @feed_description = I18n.t('models.project.feed_description', :username => current_user.display_name)
        end
        format.text do
          # somehow passing Mime[:text] using content_type to render does not work
          headers['Content-Type'] = Mime[:text].to_s
        end
        format.autocomplete do
          projects = current_user.projects.active + current_user.projects.hidden
          render :body => for_autocomplete(projects, params[:term])
        end
      end
    end
  end

  def review
    @source_view = params['_source_view'] || 'review'
    @page_title = t('projects.list_reviews')
    projects = current_user.projects
    @projects_to_review = projects.select { |p| p.needs_review?(current_user) }
    @stalled_projects = projects.select { |p| p.stalled? }
    @blocked_projects = projects.select { |p| p.blocked? }
    @current_projects = projects.uncompleted.select { |p| not p.needs_review?(current_user) }.sort_by { |p| p.last_reviewed || Time.zone.at(0) }

    init_not_done_counts(['project'])
    init_hidden_todo_counts(['project'])
    current_user.projects.cache_note_counts

    @page_title = t('projects.list_reviews')
    @count = @projects_to_review.count + @blocked_projects.count + @stalled_projects.count + @current_projects.count

    @no_projects = current_user.projects.empty?
    @new_project = current_user.projects.build
  end

  def done
    @source_view = params['_source_view'] || 'project_list'
    @page_title = t('projects.list_completed_projects')

    items_per_page = 20
    page = params[:page] || 1
    @projects = current_user.projects.completed.paginate :page => page, :per_page => items_per_page
    @count = @projects.count
    @total = current_user.projects.completed.count
    @no_projects = @projects.empty?

    @range_low = (page.to_i - 1) * items_per_page + 1
    @range_high = @range_low + @projects.size - 1

    @range_low = 0 if @total == 0
    @range_high = @total if @range_high > @total

    init_not_done_counts(['project'])
  end

  def set_reviewed
    @project.last_reviewed = Time.zone.now
    @project.save

    case @source_view
    when "project"
      redirect_to :action => 'show'
    when "project_list"
      redirect_to :action => 'index'
    when "review"
      redirect_to :action => 'review'
    else
      redirect_to :action => 'index'
    end
  end

  def projects_and_actions
    @projects = current_user.projects.active
    respond_to do |format|
      format.text do
        # somehow passing Mime[:text] using content_type to render does not work
        headers['Content-Type'] = Mime[:text].to_s
        render :action => 'index_text_projects_and_actions', :layout => false, :content_type => Mime[:text]
      end
    end
  end

  def show
    @max_completed = current_user.prefs.show_number_completed
    init_data_for_sidebar unless mobile?
    @page_title = t('projects.page_title', :project => @project.name)

    @not_done_todos = @project.todos.active_or_hidden.includes(Todo::DEFAULT_INCLUDES)
    @deferred_todos = @project.todos.deferred.includes(Todo::DEFAULT_INCLUDES)
    @pending_todos = @project.todos.pending.includes(Todo::DEFAULT_INCLUDES)
    @contexts_to_show = current_user.contexts.active
    @projects_to_show = [@project]

    @done = {}
    @done = @project.todos.completed
      .reorder("todos.completed_at DESC")
      .limit(current_user.prefs.show_number_completed)
      .includes(Todo::DEFAULT_INCLUDES) unless @max_completed == 0

    @down_count = @not_done_todos.size + @deferred_todos.size + @pending_todos.size
    @count = @down_count
    @next_project = current_user.projects.next_from(@project)
    @previous_project = current_user.projects.previous_from(@project)
    @default_tags = @project.default_tags
    @new_note = current_user.notes.new
    @new_note.project_id = @project.id
    @contexts = current_user.contexts
    respond_to do |format|
      format.html
      format.m do
        if @project.default_context.nil?
          @project_default_context = t('projects.no_default_context')
        else
          @project_default_context = t('projects.default_context', :context => @project.default_context.name)
        end
        cookies[:mobile_url] = { :value => request.fullpath, :secure => SITE_CONFIG['secure_cookies'] }
        @mobile_from_project = @project.id
      end
      format.xml do
        render :xml => @project.to_xml(:root => :project, :except => :user_id) { |xml|
          xml.not_done { @not_done_todos.each { |child| child.to_xml(:builder => xml, :skip_instruct => true) } }
          xml.deferred { @deferred_todos.each { |child| child.to_xml(:builder => xml, :skip_instruct => true) } }
          xml.pending { @pending_todos.each { |child| child.to_xml(:builder => xml, :skip_instruct => true) } }
          xml.done { @done.each { |child| child.to_xml(:builder => xml, :skip_instruct => true) } }
        }
      end
    end
  end

  def create
    if params[:format] == 'application/xml' && params['exception']
      render_failure "Expected post format is valid xml like so: <project><name>project name</name></project>.", 400
      return
    end
    @project = current_user.projects.build(project_params)
    @go_to_project = params['go_to_project']
    @saved = @project.save
    @project_not_done_counts = { @project.id => 0 }
    @active_projects_count = current_user.projects.active.count
    @contexts = current_user.contexts

    respond_to do |format|
      format.js do
        @down_count = current_user.projects.size
        init_not_done_counts
      end
      format.xml do
        if @project.new_record?
          render_failure @project.errors.full_messages.to_xml(root: "errors", skip_types: true).html_safe, 409
        else
          head :created, :location => project_url(@project), :text => @project.id
        end
      end
      format.html { redirect_to :action => 'index' }
    end
  end

  # Edit the details of the project
  def update
    template = ""

    params['project'] ||= {}
    if params['project']['state']
      @new_state = params['project']['state']
      @state_changed = @project.state != @new_state
      params['project'].delete('state')
    end
    success_text = if params['field'] == 'name' && params['value']
      params['project']['id'] = params['id']
      params['project']['name'] = params['value']
    end

    @project.attributes = project_params
    @saved = @project.save
    if @saved
      @project.transition_to(@new_state) if @state_changed
      if boolean_param('wants_render')
        @contexts = current_user.contexts
        update_state_counts
        init_data_for_sidebar
        init_hidden_todo_counts(['project'])

        template = 'projects/update'

      # TODO: are these params ever set? or is this dead code?
      elsif boolean_param('update_status')
        template = 'projects/update_status'
      elsif boolean_param('update_default_context')
        @initial_context_name = @project.default_context.name
        template = 'projects/update_default_context'
      elsif boolean_param('update_default_tags')
        template = 'projects/update_default_tags'
      elsif boolean_param('update_project_name')
        # clicking on a project name in the project view gives a form triggering this
        @projects = current_user.projects
        template = 'projects/update_project_name'
      else
        render :body => success_text || 'Success'
        return
      end
    else
      init_data_for_sidebar
      template = 'projects/update'
    end

    respond_to do |format|
      format.js { render :template => template }
      format.html { redirect_to :action => 'index' }
      format.xml do
        if @saved
          render :xml => @project.to_xml(:except => :user_id)
        else
          render :body => "Error on update: #{@project.errors.full_messages.inject("") { |v, e| v + e + " " }}", :status => 409
        end
      end
    end
  end

  def edit
    respond_to do |format|
      format.js
    end
  end

  def destroy
    @project.recurring_todos.each(&:remove_from_project!)
    @project.destroy

    respond_to do |format|
      format.js do
        @down_count = current_user.projects.size
        update_state_counts
      end
      format.xml { render :body => "Deleted project #{@project.name}" }
    end
  end

  def order
    project_ids = params["container_project"]
    @projects = current_user.projects.update_positions(project_ids)
    head :ok
  rescue
    notify :error, $!
    redirect_to :action => 'index'
  end

  def alphabetize
    @state = params['state']
    @projects = current_user.projects.alphabetize(:state => @state) if @state
    @contexts = current_user.contexts
    init_not_done_counts(['project'])
    init_hidden_todo_counts(['project']) if @state == 'hidden'
  end

  def actionize
    @state = params['state']
    @projects = current_user.projects.actionize(:state => @state) if @state
    @contexts = current_user.contexts
    init_not_done_counts(['project'])
    init_hidden_todo_counts(['project']) if @state == 'hidden'
  end

  def done_todos
    done_todos_for current_user.projects.find(params[:id])
  end

  def all_done_todos
    all_done_todos_for current_user.projects.find(params[:id])
  end

  protected

  def update_state_counts
    @active_projects_count = current_user.projects.active.count
    @hidden_projects_count = current_user.projects.hidden.count
    @completed_projects_count = current_user.projects.completed.count
    @show_active_projects = @active_projects_count > 0
    @show_hidden_projects = @hidden_projects_count > 0
    @show_completed_projects = @completed_projects_count > 0
  end

  def set_project_from_params
    @project = current_user.projects.find_by_params(params)
  end

  def set_source_view
    @source_view = params['_source_view'] || 'project'
  end

  def default_context_filter
    p = params['project']
    p = params['request']['project'] if p.nil? && params['request']
    p = {} if p.nil?
    default_context_name = p['default_context_name']
    p.delete('default_context_name')

    if default_context_name.present?
      default_context = current_user.contexts.where(:name => default_context_name).first_or_create
      p['default_context_id'] = default_context.id
    else
      p['default_context_id'] = nil
    end
  end

  private

  def project_params
    params.require(:project).permit(:name, :position, :user_id, :description, :state, :default_context_id, :default_tags)
  end
end