#-- encoding: UTF-8
#-- copyright
# OpenProject is a project management system.
# Copyright (C) 2012-2015 the OpenProject Foundation (OPF)
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See doc/COPYRIGHT.rdoc for more details.
#++
module WorkPackagesHelper
include AccessibilityHelper
def work_package_breadcrumb
full_path = if !@project.nil?
link_to(I18n.t(:label_work_package_plural), project_path(@project, jump: current_menu_item))
else
ancestors_links.unshift(work_package_index_link)
end
breadcrumb_paths(*full_path)
end
def ancestors_links
controller.ancestors.map do |parent|
link_to '#' + h(parent.id), work_package_path(parent.id)
end
end
def work_package_index_link
# TODO: will need to change to work_package index
link_to(I18n.t(:label_work_package_plural), controller: :work_packages, action: :index)
end
# Displays a link to +work_package+ with its subject.
# Examples:
#
# link_to_work_package(package) # => Defect #6: This is the subject
# link_to_work_package(package, :all_link => true) # => Defect #6: This is the subject (everything within the link)
# link_to_work_package(package, :truncate => 9) # => Defect #6: This i...
# link_to_work_package(package, :subject => false) # => Defect #6
# link_to_work_package(package, :type => false) # => #6: This is the subject
# link_to_work_package(package, :project => true) # => Foo - Defect #6
# link_to_work_package(package, :id_only => true) # => #6
# link_to_work_package(package, :subject_only => true) # => This is the subject (as link)
# link_to_work_package(package, :status => true) # => #6 New (if #id => true)
def link_to_work_package(package, options = {})
if options[:subject_only]
options.merge!(type: false,
subject: true,
id: false,
all_link: true)
elsif options[:id_only]
options.merge!(type: false,
subject: false,
id: true,
all_link: true)
else
options.reverse_merge!(type: true,
subject: true,
id: true)
end
parts = { prefix: [],
hidden_link: [],
link: [],
suffix: [],
title: [],
css_class: ['issue'] }
# Prefix part
parts[:prefix] << "#{package.project}" if options[:project]
# Link part
parts[:link] << h(options[:before_text].to_s) if options[:before_text]
parts[:link] << h(package.kind.to_s) if options[:type]
parts[:link] << "##{h(package.id)}" if options[:id]
parts[:link] << "#{h(package.status)}" if options[:id] && options[:status] && package.status
# Hidden link part
if package.closed?
parts[:hidden_link] << content_tag(:span,
I18n.t(:label_closed_work_packages),
class: 'hidden-for-sighted')
parts[:css_class] << 'closed'
end
# Suffix part
if options[:subject]
subject = if options[:subject]
subject = package.subject
if options[:truncate]
subject = truncate(subject, length: options[:truncate])
end
subject
end
parts[:suffix] << h(subject)
end
# title part
parts[:title] << package.subject
# combining
prefix = parts[:prefix].join(' ')
suffix = parts[:suffix].join(' ')
link = parts[:link].join(' ').strip
hidden_link = parts[:hidden_link].join('')
title = parts[:title].join(' ')
css_class = parts[:css_class].join(' ')
text = if options[:all_link]
link_text = [prefix, link].reject(&:empty?).join(' - ')
link_text = [link_text, suffix].reject(&:empty?).join(': ')
link_text = [hidden_link, link_text].reject(&:empty?).join('')
link_to(link_text.html_safe,
work_package_path(package),
title: title,
class: css_class)
else
link_text = [hidden_link, link].reject(&:empty?).join('')
html_link = link_to(link_text.html_safe,
work_package_path(package),
title: title,
class: css_class)
[[prefix, html_link].reject(&:empty?).join(' - '),
suffix].reject(&:empty?).join(': ')
end.html_safe
end
def work_package_quick_info(work_package)
changed_dates = {}
journals = work_package.journals.where(['created_at >= ?', Date.today.to_time - 7.day])
.order('created_at desc')
journals.each do |journal|
break if changed_dates['start_date'] && changed_dates['due_date']
['start_date', 'due_date'].each do |date|
if changed_dates[date].nil? &&
journal.changed_data[date] &&
journal.changed_data[date].first
changed_dates[date] = " (#{journal.changed_data[date].first})".html_safe
end
end
end
link = link_to_work_package(work_package, status: true)
link += " #{work_package.start_date.nil? ? '[?]' : work_package.start_date.to_s}"
link += changed_dates['start_date']
link += " – #{work_package.due_date.nil? ? '[?]' : work_package.due_date.to_s}"
link += changed_dates['due_date']
link
end
def work_package_quick_info_with_description(work_package, lines = 3)
description = truncated_work_package_description(work_package, lines)
link = work_package_quick_info(work_package)
attributes = info_user_attributes(work_package)
link += content_tag(:div, attributes, class: 'indent quick_info attributes')
link += content_tag(:div, description, class: 'indent quick_info description')
link
end
def work_package_list(work_packages, &_block)
ancestors = []
work_packages.each do |work_package|
while ancestors.any? && !work_package.is_descendant_of?(ancestors.last)
ancestors.pop
end
yield work_package, ancestors.size
ancestors << work_package unless work_package.leaf?
end
end
def send_notification_option(checked = true)
work_package_form_field do
field = content_tag(:label,
l(:label_notify_member_plural),
for: 'send_notification',
class: 'form--label')
field += content_tag 'span', class: 'form--field-container' do
content_tag 'span', class: 'form--check-box-field' do
boxes = hidden_field_tag('send_notification', '0', id: nil)
boxes += check_box_tag('send_notification',
'1',
checked,
class: 'form--check-box')
boxes
end
end
field
end
end
def render_work_package_tree_row(work_package, level, relation)
css_classes = ['work-package']
css_classes << "work-package-#{work_package.id}"
css_classes << 'idnt' << "idnt-#{level}" if level > 0
if relation == 'root'
issue_text = link_to("#{work_package}",
'javascript:void(0)',
style: 'color:inherit; font-weight: bold; text-decoration:none; cursor:default;')
else
title = []
if relation == 'parent'
title << content_tag(:span, l(:description_parent_work_package), class: 'hidden-for-sighted')
elsif relation == 'child'
title << content_tag(:span, l(:description_sub_work_package), class: 'hidden-for-sighted')
end
issue_text = link_to(work_package.to_s.html_safe, work_package_path(work_package))
end
content_tag :tr, class: css_classes.join(' ') do
concat content_tag :td, check_box_tag('ids[]', work_package.id, false, id: nil), class: 'checkbox'
concat content_tag :td, issue_text, class: 'subject'
concat content_tag :td, h(work_package.status)
concat content_tag :td, link_to_user(work_package.assigned_to)
concat content_tag :td, link_to_version(work_package.fixed_version)
end
end
# Returns a string of css classes that apply to the issue
def work_package_css_classes(work_package)
# TODO: remove issue once css is cleaned of it
s = 'issue work_package'.html_safe
s << " status-#{work_package.status.position}" if work_package.status
s << " priority-#{work_package.priority.position}" if work_package.priority
s << ' closed' if work_package.closed?
s << ' overdue' if work_package.overdue?
s << ' child' if work_package.child?
s << ' parent' unless work_package.leaf?
s << ' created-by-me' if User.current.logged? && work_package.author_id == User.current.id
s << ' assigned-to-me' if User.current.logged? && work_package.assigned_to_id == User.current.id
s
end
WorkPackageAttribute = Struct.new(:attribute, :field)
def work_package_form_all_middle_attributes(form, work_package, locals = {})
[
work_package_form_status_attribute(form, work_package, locals),
work_package_form_priority_attribute(form, work_package, locals),
work_package_form_assignee_attribute(form, work_package, locals),
work_package_form_responsible_attribute(form, work_package, locals),
work_package_form_category_attribute(form, work_package, locals),
work_package_form_assignable_versions_attribute(form, work_package, locals),
work_package_form_start_date_attribute(form, work_package, locals),
work_package_form_due_date_attribute(form, work_package, locals),
work_package_form_estimated_hours_attribute(form, work_package, locals),
work_package_form_done_ratio_attribute(form, work_package, locals),
work_package_form_custom_values_attribute(form, work_package, locals)
].flatten.compact
end
def work_package_form_minimal_middle_attributes(form, work_package, locals = {})
[
work_package_form_status_attribute(form, work_package, locals),
work_package_form_assignee_attribute(form, work_package, locals),
work_package_form_assignable_versions_attribute(form, work_package, locals),
work_package_form_done_ratio_attribute(form, work_package, locals),
].flatten.compact
end
def work_package_form_top_attributes(form, work_package, locals = {})
[
work_package_form_type_attribute(form, work_package, locals),
work_package_form_subject_attribute(form, work_package, locals),
work_package_form_parent_attribute(form, work_package, locals),
work_package_form_description_attribute(form, work_package, locals)
].compact
end
def work_package_show_attribute_list(work_package)
main_attributes = work_package_show_main_attributes(work_package)
custom_field_attributes = work_package_show_custom_fields(work_package)
core_attributes = (main_attributes | custom_field_attributes).compact
hook_attributes(work_package, core_attributes).compact
end
def group_work_package_attributes(attribute_list)
attributes = {}
attributes[:left], attributes[:right] = attribute_list.each_slice((attribute_list.count + 1) / 2).to_a
attributes
end
def work_package_show_attributes(work_package)
group_work_package_attributes work_package_show_attribute_list(work_package)
end
def work_package_show_table_row(attribute, klass = nil, attribute_lang = nil, value_lang = nil, &block)
klass = attribute.to_s.dasherize if klass.nil?
attribute_string = if attribute.is_a?(Symbol)
WorkPackage.human_attribute_name(attribute)
else
attribute
end
content = content_tag(:td, class: [:work_package_attribute_header, klass], lang: attribute_lang) { "#{attribute_string}:" }
content << content_tag(:td, class: klass, lang: value_lang, &block)
WorkPackageAttribute.new(attribute, content)
end
def work_package_show_status_attribute(work_package)
work_package_show_table_row(:status) do
work_package.status ?
work_package.status.name :
empty_element_tag
end
end
def work_package_show_start_date_attribute(work_package)
work_package_show_table_row(:start_date, 'start-date') do
work_package.start_date ?
format_date(work_package.start_date) :
empty_element_tag
end
end
def work_package_show_priority_attribute(work_package)
work_package_show_table_row(:priority) do
work_package.priority ?
work_package.priority.name :
empty_element_tag
end
end
def work_package_show_due_date_attribute(work_package)
work_package_show_table_row(:due_date) do
work_package.due_date ?
format_date(work_package.due_date) :
empty_element_tag
end
end
def work_package_show_assigned_to_attribute(work_package)
work_package_show_table_row(:assigned_to) do
content = avatar(work_package.assigned_to, class: 'avatar-mini').html_safe
content << (work_package.assigned_to ? link_to_user(work_package.assigned_to) : empty_element_tag)
content
end
end
def work_package_show_responsible_attribute(work_package)
work_package_show_table_row(:responsible) do
content = avatar(work_package.responsible, class: 'avatar-mini').html_safe
content << (work_package.responsible ? link_to_user(work_package.responsible) : empty_element_tag)
content
end
end
def work_package_show_progress_attribute(work_package)
return if WorkPackage.done_ratio_disabled?
work_package_show_table_row(:progress, 'done-ratio') do
progress_bar work_package.done_ratio, width: '80px', legend: work_package.done_ratio.to_s
end
end
def work_package_show_category_attribute(work_package)
work_package_show_table_row(:category) do
work_package.category ?
work_package.category.name :
empty_element_tag
end
end
def work_package_show_spent_time_attribute(work_package)
work_package_show_table_row(:spent_time) do
work_package.spent_hours > 0 ?
link_to(l_hours(work_package.spent_hours), work_package_time_entries_path(work_package)) :
empty_element_tag
end
end
def work_package_show_fixed_version_attribute(work_package)
work_package_show_table_row(:fixed_version) do
work_package.fixed_version ?
link_to_version(work_package.fixed_version) :
empty_element_tag
end
end
def work_package_show_estimated_hours_attribute(work_package)
work_package_show_table_row(:estimated_hours) do
work_package.estimated_hours ?
l_hours(work_package.estimated_hours) :
empty_element_tag
end
end
def work_package_form_field(required: false, classes: '')
div_class = 'form--field'
div_class << " #{classes}"
div_class << ' -required' if required
content_tag 'div', class: div_class do
yield
end
end
def work_package_form_type_attribute(form, work_package, locals = {})
selectable_types = locals[:project].types.map { |t| [((t.is_standard) ? '' : t.name), t.id] }
field = work_package_form_field do
form.select :type_id, selectable_types
end
url = work_package.new_record? ?
new_type_project_work_packages_path(locals[:project]) :
new_type_work_package_path(work_package)
field += observe_field :work_package_type_id, url: url,
update: :attributes,
method: :get,
with: "Form.serialize('work_package-form')"
WorkPackageAttribute.new(:type, field)
end
def work_package_form_subject_attribute(form, _work_package, _locals = {})
field = work_package_form_field do
form.text_field(:subject, required: true)
end
WorkPackageAttribute.new :subject, field
end
def work_package_form_parent_attribute(form, work_package, locals = {})
return unless User.current.allowed_to?(:manage_subtasks, locals[:project])
parent_field = work_package_form_field do
form.text_field :parent_id,
size: 10,
title: l(:description_autocomplete),
class: 'short'
end
parent_field += '