262 lines
7.4 KiB
Ruby
262 lines
7.4 KiB
Ruby
class EasyGanttController < ApplicationController
|
|
accept_api_auth :index, :issues, :projects, :project_issues, :change_issue_relation_delay, :reschedule_project
|
|
menu_item :easy_gantt
|
|
|
|
RELATION_TYPES_TO_LOAD = ['relates', 'blocks', 'blocked', 'precedes', 'follows', 'start_to_start', 'finish_to_finish', 'start_to_finish']
|
|
|
|
before_action :find_optional_project, except: [:reschedule_project, :project_issues]
|
|
before_action :find_opened_project, except: [:reschedule_project]
|
|
|
|
before_action :authorize, if: proc { @project.present? }
|
|
before_action :authorize_global, if: proc { @project.nil? }
|
|
|
|
before_action :check_rest_api_enabled, only: [:index]
|
|
before_action :find_relation, only: [:change_issue_relation_delay]
|
|
|
|
helper :queries
|
|
include QueriesHelper
|
|
helper :sort
|
|
include SortHelper
|
|
helper :custom_fields
|
|
helper :icons
|
|
|
|
def index
|
|
retrieve_query
|
|
end
|
|
|
|
# Data retrieve method
|
|
def issues
|
|
retrieve_query
|
|
|
|
load_projects
|
|
load_issues
|
|
load_versions
|
|
load_relations
|
|
build_dates @issues, :start_date, :due_date
|
|
end
|
|
|
|
# Data retrieve method
|
|
def projects
|
|
retrieve_query
|
|
|
|
load_projects
|
|
build_dates @projects, :gantt_start_date, :gantt_due_date
|
|
|
|
@projects_issues_counts = Issue.visible.gantt_opened.where(project_id: @projects).group(:project_id).count(:id)
|
|
end
|
|
|
|
def project_issues
|
|
# TODO: Global route to skip rights
|
|
@issues = Issue.visible.gantt_opened.where(project_id: params[:project_id]).order(:start_date)
|
|
@issue_ids = @issues.map(&:id)
|
|
load_relations
|
|
|
|
version_ids = @issues.map(&:fixed_version_id).uniq.compact
|
|
@versions = Version.open.where('id IN (?) OR project_id = ?', version_ids, params[:project_id]).sorted
|
|
end
|
|
|
|
def change_issue_relation_delay
|
|
if !User.current.allowed_to?(:manage_issue_relations, @project)
|
|
return render_403
|
|
end
|
|
|
|
@relation.update_column(:delay, params[:delay].to_i)
|
|
|
|
respond_to do |format|
|
|
format.api { render_api_ok }
|
|
end
|
|
end
|
|
|
|
# You cannot use issue.reschedule_on because it will
|
|
# also set start_date which is not desirable !!!
|
|
def reschedule_project
|
|
begin
|
|
# Do not used callback `find_project` because it will test access rights
|
|
# to project context. Method wont work if project does not have gantt enabled.
|
|
project = Project.find(params[:id])
|
|
rescue ActiveRecord::RecordNotFound
|
|
render_404
|
|
return
|
|
end
|
|
|
|
project.gantt_reschedule(params[:days].to_i)
|
|
|
|
respond_to do |format|
|
|
format.api { render_api_ok }
|
|
end
|
|
end
|
|
|
|
def current_menu_item
|
|
@current_menu_item ||= if params[:gantt_type] == 'rm'
|
|
:resource
|
|
else
|
|
:easy_gantt
|
|
end
|
|
end
|
|
|
|
private
|
|
|
|
def check_rest_api_enabled
|
|
if Setting.rest_api_enabled != '1'
|
|
render_error message: l('easy_gantt.errors.no_rest_api')
|
|
return false
|
|
end
|
|
end
|
|
|
|
def find_relation
|
|
@relation = IssueRelation.find(params[:id])
|
|
rescue ActiveRecord::RecordNotFound
|
|
render_404
|
|
end
|
|
|
|
def query_class
|
|
@project ? EasyGantt::EasyGanttIssueQuery : EasyGantt::EasyGanttProjectQuery
|
|
end
|
|
|
|
def retrieve_query
|
|
if params[:query_id].present?
|
|
cond = 'project_id IS NULL'
|
|
|
|
if @project
|
|
cond << " OR project_id = #{@project.id}"
|
|
end
|
|
|
|
@query = query_class.where(cond).find_by(id: params[:query_id])
|
|
raise ActiveRecord::RecordNotFound if @query.nil?
|
|
raise Unauthorized unless @query.visible?
|
|
|
|
@query.project = @project
|
|
sort_clear
|
|
else
|
|
@query = query_class.new(name: '_')
|
|
@query.project = @project
|
|
@query.from_params(params)
|
|
end
|
|
|
|
if @opened_project
|
|
@query.opened_project = @opened_project
|
|
end
|
|
end
|
|
|
|
# Load version from loaded task and from opened projects
|
|
# TO_CONSIDER: Send versions if there is no tasks?
|
|
def load_versions
|
|
version_ids = @issues.map(&:fixed_version_id).uniq.compact
|
|
@versions = Version.open.where("id IN (?) OR project_id = ?", version_ids, @opened_project.id).sorted
|
|
end
|
|
|
|
# Load subproject of opened project which contains filtered tasks
|
|
#
|
|
# Project 1
|
|
# |-- Project 1.1
|
|
# | `-- Project 1.1.1
|
|
# | |-- Task 1
|
|
# | `-- Task 2
|
|
# `-- Project 1.2
|
|
#
|
|
# If Project 1 is opened, Project 1.1 must be send even if there is no task
|
|
#
|
|
# TO_CONSIDER: Send full tree and load only opened_project's issues
|
|
#
|
|
def load_projects
|
|
p_table = Project.table_name
|
|
|
|
@projects = []
|
|
|
|
# Project gantt is opened, normally only subprojects will be sent
|
|
# but there is not any root project yet
|
|
if @project && @opened_project == @project
|
|
@projects << @project
|
|
end
|
|
|
|
projects = @query.without_opened_project { |q|
|
|
scope = q.create_entity_scope
|
|
|
|
# Not necessary, will take only subprojects
|
|
if @opened_project
|
|
scope = scope.where("#{p_table}.lft >= ? AND #{p_table}.rgt <= ?", @opened_project.lft, @opened_project.rgt)
|
|
end
|
|
|
|
scope.reorder(nil).distinct.pluck("#{p_table}.lft, #{p_table}.rgt, #{p_table}.parent_id")
|
|
}
|
|
|
|
if projects.blank?
|
|
return
|
|
end
|
|
|
|
# All ancestors conditions
|
|
tree_conditions = []
|
|
projects.each do |lft, rgt|
|
|
tree_conditions << "(lft <= #{lft} AND rgt >= #{rgt})"
|
|
end
|
|
tree_conditions = tree_conditions.join(' OR ')
|
|
|
|
@parent_ids = projects.map(&:last)
|
|
|
|
# From ancestors take only current opened level
|
|
@projects.concat Project.where(tree_conditions).where(parent_id: @opened_project.try(:id)).to_a
|
|
|
|
Project.load_gantt_dates(@projects)
|
|
if Setting.plugin_easy_gantt['show_project_progress'] == '1'
|
|
Project.load_gantt_completed_percent(@projects)
|
|
end
|
|
end
|
|
|
|
# Only between loaded tasks
|
|
def load_relations
|
|
if @issue_ids.empty?
|
|
@relations = []
|
|
else
|
|
@relations = IssueRelation.where('issue_from_id IN (?) OR issue_to_id IN (?)', @issue_ids, @issue_ids).
|
|
where(relation_type: RELATION_TYPES_TO_LOAD)
|
|
end
|
|
end
|
|
|
|
def load_issues
|
|
preloads = [:project, :author, :assigned_to, :relations_to]
|
|
|
|
if Setting.plugin_easy_gantt['show_task_soonest_start'] == '1'
|
|
preloads << :parent
|
|
preloads << { relations_to: :issue_from }
|
|
else
|
|
preloads << :relations_to
|
|
end
|
|
|
|
@issues = @query.entities(
|
|
includes: [:project, :status, :assigned_to, :fixed_version, :tracker, :priority, :custom_values],
|
|
preload: preloads,
|
|
order: "#{Issue.table_name}.start_date, #{Issue.table_name}.id"
|
|
)
|
|
|
|
@issue_ids = @issues.map(&:id)
|
|
end
|
|
|
|
def build_dates(data, starts, ends)
|
|
starts = data.map(&starts).compact
|
|
ends = data.map(&ends).compact
|
|
|
|
@start_date = (starts.min || ends.min || Date.today) - 1.day
|
|
@end_date = (ends.max || starts.max || Date.today) + 1.day
|
|
end
|
|
|
|
def find_optional_project
|
|
# Easy query workaround
|
|
if params[:set_filter] == '1' && params[:project_id].present? && params[:project_id].start_with?('=', '!*', '*')
|
|
return
|
|
end
|
|
|
|
super
|
|
end
|
|
|
|
def find_opened_project
|
|
if params[:opened_project_id].present?
|
|
@opened_project = Project.find(params[:opened_project_id])
|
|
else
|
|
@opened_project = @project
|
|
end
|
|
rescue ActiveRecord::RecordNotFound
|
|
render_404
|
|
end
|
|
|
|
end
|