Actualizar plugin Additionals a 3.0.0
This commit is contained in:
parent
3d976f1b3b
commit
a26f5567af
399 changed files with 70374 additions and 4093 deletions
|
@ -0,0 +1,12 @@
|
|||
module AdditionalsChartjsHelper
|
||||
def chartjs_colorschemes_info_url
|
||||
link_to(l(:label_chartjs_colorscheme_info),
|
||||
'https://nagix.github.io/chartjs-plugin-colorschemes/colorchart.html',
|
||||
class: 'external')
|
||||
end
|
||||
|
||||
def select_options_for_chartjs_colorscheme(selected)
|
||||
data = YAML.safe_load(ERB.new(IO.read(Rails.root.join('plugins/additionals/config/colorschemes.yml'))).result) || {}
|
||||
grouped_options_for_select(data, selected)
|
||||
end
|
||||
end
|
|
@ -0,0 +1,25 @@
|
|||
module AdditionalsClipboardjsHelper
|
||||
def clipboardjs_button_for(target, clipboard_text_from_button = nil)
|
||||
render_clipboardjs_button(target, clipboard_text_from_button) + render_clipboardjs_javascript(target)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def render_clipboardjs_button(target, clipboard_text_from_button)
|
||||
data = { 'clipboard-target' => "##{target}",
|
||||
'label-copied' => l(:label_copied_to_clipboard),
|
||||
'label-to-copy' => l(:label_copy_to_clipboard) }
|
||||
|
||||
data['clipboard-text'] = clipboard_text_from_button if clipboard_text_from_button.present?
|
||||
|
||||
tag.button id: "zc_#{target}",
|
||||
class: 'clipboard_button far fa-copy',
|
||||
type: 'button',
|
||||
title: l(:label_copy_to_clipboard),
|
||||
data: data
|
||||
end
|
||||
|
||||
def render_clipboardjs_javascript(target)
|
||||
javascript_tag("setClipboardJS('#zc_#{target}');")
|
||||
end
|
||||
end
|
|
@ -0,0 +1,5 @@
|
|||
module AdditionalsCustomFieldsHelper
|
||||
def custom_fields_with_full_with_layout
|
||||
['IssueCustomField']
|
||||
end
|
||||
end
|
|
@ -1,11 +1,4 @@
|
|||
module AdditionalsFontawesomeHelper
|
||||
def fontawesome_info_url
|
||||
s = []
|
||||
s << l(:label_set_icon_from)
|
||||
s << link_to('https://fontawesome.com/icons?m=free', 'https://fontawesome.com/icons?m=free', class: 'external')
|
||||
safe_join(s, ' ')
|
||||
end
|
||||
|
||||
# name = TYPE-FA_NAME, eg. fas_car
|
||||
# fas_cloud-upload-alt
|
||||
# far_id-card
|
||||
|
@ -15,13 +8,13 @@ module AdditionalsFontawesomeHelper
|
|||
# post_text
|
||||
# title
|
||||
def font_awesome_icon(name, options = {})
|
||||
info = AdditionalsFontAwesome.value_info(name)
|
||||
info = AdditionalsFontAwesome.value_info name
|
||||
return '' if info.blank?
|
||||
|
||||
post_text = ''
|
||||
options['aria-hidden'] = 'true'
|
||||
options[:'aria-hidden'] = 'true'
|
||||
options[:class] = if options[:class].present?
|
||||
info[:classes] + ' ' + options[:class]
|
||||
"#{info[:classes]} #{options[:class]}"
|
||||
else
|
||||
info[:classes]
|
||||
end
|
||||
|
@ -30,17 +23,68 @@ module AdditionalsFontawesomeHelper
|
|||
if options[:pre_text].present?
|
||||
s << options[:pre_text]
|
||||
s << ' '
|
||||
options.delete(:pre_text)
|
||||
options.delete :pre_text
|
||||
end
|
||||
if options[:post_text].present?
|
||||
post_text = options[:post_text]
|
||||
options.delete(:post_text)
|
||||
options.delete :post_text
|
||||
end
|
||||
s << content_tag('span', '', options)
|
||||
s << tag.span(options)
|
||||
if post_text.present?
|
||||
s << ' '
|
||||
s << post_text
|
||||
end
|
||||
safe_join(s)
|
||||
safe_join s
|
||||
end
|
||||
|
||||
def additionals_fontawesome_select(form, selected, options = {})
|
||||
options[:include_blank] ||= true unless options[:required]
|
||||
html_options = {}
|
||||
|
||||
additionals_fontawesome_add_selected selected
|
||||
|
||||
name, options = Additionals.hash_remove_with_default(:name, options, :icon)
|
||||
loader, options = Additionals.hash_remove_with_default(:loader, options, true)
|
||||
html_options[:class], options = Additionals.hash_remove_with_default(:class, options, 'select2-fontawesome-field')
|
||||
html_options[:style], options = Additionals.hash_remove_with_default(:style, options)
|
||||
|
||||
s = []
|
||||
s << form.select(name,
|
||||
options_for_select(AdditionalsFontAwesome.active_option_for_select(selected), selected),
|
||||
options,
|
||||
html_options)
|
||||
|
||||
s << additionals_fontawesome_loader(options, html_options) if loader
|
||||
|
||||
safe_join s
|
||||
end
|
||||
|
||||
def additionals_fontawesome_add_selected(selected)
|
||||
@selected_store ||= []
|
||||
return if selected.blank?
|
||||
|
||||
@selected_store << selected
|
||||
end
|
||||
|
||||
def additionals_fontawesome_default_select_width
|
||||
'250px'
|
||||
end
|
||||
|
||||
def additionals_fontawesome_loader(options, html_options = {})
|
||||
html_options[:class] ||= 'select2-fontawesome-field'
|
||||
options[:template_selection] = 'formatFontawesomeText'
|
||||
options[:template_result] = 'formatFontawesomeText'
|
||||
if options[:include_blank]
|
||||
options[:placeholder] ||= l(:label_disabled)
|
||||
options[:allow_clear] ||= true
|
||||
end
|
||||
options[:width] = additionals_fontawesome_default_select_width
|
||||
|
||||
render(layout: false,
|
||||
partial: 'additionals/select2_ajax_call.js',
|
||||
formats: [:js],
|
||||
locals: { field_class: html_options[:class],
|
||||
ajax_url: fontawesome_auto_completes_path(selected: @selected_store.join(',')),
|
||||
options: options })
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,18 +1,29 @@
|
|||
module AdditionalsIssuesHelper
|
||||
def issue_author_options_for_select(project, issue = nil)
|
||||
authors = project.users.sorted
|
||||
def author_options_for_select(project, entity = nil, permission = nil)
|
||||
scope = project.present? ? project.users.visible : User.active.visible
|
||||
scope = scope.with_permission(permission, project) unless permission.nil?
|
||||
authors = scope.sorted.to_a
|
||||
|
||||
unless entity.nil?
|
||||
current_author_found = authors.detect { |u| u.id == entity.author_id_was }
|
||||
if current_author_found.blank?
|
||||
current_author = User.find_by id: entity.author_id_was
|
||||
authors << current_author if current_author
|
||||
end
|
||||
end
|
||||
|
||||
s = []
|
||||
return s unless authors.any?
|
||||
|
||||
s << content_tag('option', "<< #{l(:label_me)} >>", value: User.current.id) if authors.include?(User.current)
|
||||
s << tag.option("<< #{l :label_me} >>", value: User.current.id) if authors.include?(User.current)
|
||||
|
||||
if issue.nil?
|
||||
if entity.nil?
|
||||
s << options_from_collection_for_select(authors, 'id', 'name')
|
||||
else
|
||||
s << content_tag('option', issue.author, value: issue.author_id, selected: true) if issue.author && !authors.include?(issue.author)
|
||||
s << options_from_collection_for_select(authors, 'id', 'name', issue.author_id)
|
||||
s << tag.option(entity.author, value: entity.author_id, selected: true) if entity.author && authors.exclude?(entity.author)
|
||||
s << options_from_collection_for_select(authors, 'id', 'name', entity.author_id)
|
||||
end
|
||||
safe_join(s)
|
||||
safe_join s
|
||||
end
|
||||
|
||||
def show_issue_change_author?(issue)
|
||||
|
|
159
plugins/additionals/app/helpers/additionals_journals_helper.rb
Normal file
159
plugins/additionals/app/helpers/additionals_journals_helper.rb
Normal file
|
@ -0,0 +1,159 @@
|
|||
module AdditionalsJournalsHelper
|
||||
MultipleValuesDetail = Struct.new(:property, :prop_key, :custom_field, :old_value, :value)
|
||||
|
||||
# Returns the textual representation of a journal details
|
||||
# as an array of strings
|
||||
def entity_details_to_strings(entity, details, options = {})
|
||||
entity_type = entity.model_name.param_key
|
||||
show_detail_method = "#{entity_type}_show_detail"
|
||||
options[:only_path] = options[:only_path] != false
|
||||
no_html = options.delete(:no_html)
|
||||
strings = []
|
||||
values_by_field = {}
|
||||
|
||||
details.each do |detail|
|
||||
if detail.property == 'cf'
|
||||
field = detail.custom_field
|
||||
if field&.multiple?
|
||||
values_by_field[field] ||= { added: [], deleted: [] }
|
||||
values_by_field[field][:deleted] << detail.old_value if detail.old_value
|
||||
values_by_field[field][:added] << detail.value if detail.value
|
||||
next
|
||||
end
|
||||
end
|
||||
strings << send(show_detail_method, detail, no_html, options)
|
||||
end
|
||||
|
||||
if values_by_field.present?
|
||||
values_by_field.each do |field, changes|
|
||||
if changes[:added].any?
|
||||
detail = MultipleValuesDetail.new('cf', field.id.to_s, field)
|
||||
detail.value = changes[:added]
|
||||
strings << send(show_detail_method, detail, no_html, options)
|
||||
end
|
||||
next unless changes[:deleted].any?
|
||||
|
||||
detail = MultipleValuesDetail.new('cf', field.id.to_s, field)
|
||||
detail.old_value = changes[:deleted]
|
||||
strings << send(show_detail_method, detail, no_html, options)
|
||||
end
|
||||
end
|
||||
strings
|
||||
end
|
||||
|
||||
# taken from Redmine 4
|
||||
# Returns the action links for an issue journal
|
||||
def render_entity_journal_actions(entity, journal)
|
||||
return '' unless journal.notes.present? && journal.editable_by?(User.current)
|
||||
|
||||
entity_type = entity.model_name.param_key
|
||||
|
||||
safe_join [link_to(l(:button_edit),
|
||||
send("edit_#{entity_type}_journal_path", journal),
|
||||
remote: true,
|
||||
method: 'get',
|
||||
title: l(:button_edit),
|
||||
class: 'icon-only icon-edit'),
|
||||
link_to(l(:button_delete),
|
||||
send("#{entity_type}_journal_path", journal, journal: { notes: '' }),
|
||||
remote: true,
|
||||
method: 'put', data: { confirm: l(:text_are_you_sure) },
|
||||
title: l(:button_delete),
|
||||
class: 'icon-only icon-del')], ' '
|
||||
end
|
||||
|
||||
# Returns the textual representation of a single journal detail
|
||||
# rubocop: disable Rails/OutputSafety
|
||||
def entity_show_detail(entity, detail, no_html = false, options = {}) # rubocop:disable Style/OptionalBooleanParameter:
|
||||
multiple = false
|
||||
no_detail = false
|
||||
show_diff = false
|
||||
label = nil
|
||||
diff_url_method = "diff_#{entity.name.underscore}_journal_url"
|
||||
entity_prop = entity_show_detail_prop detail, options
|
||||
|
||||
if entity_prop.present?
|
||||
label = entity_prop[:label] if entity_prop.key? :label
|
||||
value = entity_prop[:value] if entity_prop.key? :value
|
||||
old_value = entity_prop[:old_value] if entity_prop.key? :old_value
|
||||
show_diff = entity_prop[:show_diff] if entity_prop.key? :show_diff
|
||||
no_detail = entity_prop[:no_detail] if entity_prop.key? :no_detail
|
||||
end
|
||||
|
||||
if label || show_diff
|
||||
unless no_html
|
||||
label = tag.strong(label)
|
||||
old_value = tag.i(old_value) if detail.old_value
|
||||
old_value = tag.del(old_value) if detail.old_value && detail.value.blank?
|
||||
value = tag.i(value) if value
|
||||
end
|
||||
|
||||
html =
|
||||
if no_detail
|
||||
l(:text_journal_changed_no_detail, label: label)
|
||||
elsif show_diff
|
||||
s = l(:text_journal_changed_no_detail, label: label)
|
||||
unless no_html
|
||||
diff_link = link_to l(:label_diff),
|
||||
send(diff_url_method,
|
||||
detail.journal_id,
|
||||
detail_id: detail.id,
|
||||
only_path: options[:only_path]),
|
||||
title: l(:label_view_diff)
|
||||
s << " (#{diff_link})"
|
||||
end
|
||||
s.html_safe
|
||||
elsif detail.value.present?
|
||||
if detail.old_value.present?
|
||||
l(:text_journal_changed, label: label, old: old_value, new: value)
|
||||
elsif multiple
|
||||
l(:text_journal_added, label: label, value: value)
|
||||
else
|
||||
l(:text_journal_set_to, label: label, value: value)
|
||||
end
|
||||
else
|
||||
l(:text_journal_deleted, label: label, old: old_value).html_safe
|
||||
end
|
||||
html.html_safe
|
||||
else
|
||||
# default implementation for journal detail rendering
|
||||
show_detail detail, no_html, options
|
||||
end
|
||||
end
|
||||
# rubocop: enable Rails/OutputSafety
|
||||
|
||||
private
|
||||
|
||||
def entity_show_detail_prop(detail, options)
|
||||
return options[:entity_prop] if options.key? :entity_prop
|
||||
return unless detail.property == 'cf'
|
||||
|
||||
custom_field = detail.custom_field
|
||||
return unless custom_field
|
||||
|
||||
return { show_diff: true, label: l(:field_description) } if custom_field.format.class.change_as_diff
|
||||
|
||||
case custom_field.format.name
|
||||
when 'project_relation'
|
||||
prop = { label: custom_field.name }
|
||||
project = Project.visible.where(id: detail.value).first if detail.value.present?
|
||||
old_project = Project.visible.where(id: detail.old_value).first if detail.old_value.present?
|
||||
prop[:value] = link_to_project(project) if project.present?
|
||||
prop[:old_value] = link_to_project(old_project) if old_project.present?
|
||||
when 'db_entry'
|
||||
prop = { label: custom_field.name }
|
||||
db_entry = DbEntry.visible.where(id: detail.value).first if detail.value.present?
|
||||
old_db_entry = DbEntry.visible.where(id: detail.old_value).first if detail.old_value.present?
|
||||
prop[:value] = link_to(db_entry.name, db_entry_url(db_entry)) if db_entry.present?
|
||||
prop[:old_value] = link_to(old_db_entry.name, db_entry_url(old_db_entry)) if old_db_entry.present?
|
||||
when 'password'
|
||||
prop = { label: custom_field.name }
|
||||
password = Password.visible.where(id: detail.value).first if detail.value.present? && defined?(Password)
|
||||
old_password = Password.visible.where(id: detail.old_value).first if detail.old_value.present? && defined?(Password)
|
||||
prop[:value] = link_to(password.name, password_url(password)) if password.present?
|
||||
prop[:old_value] = link_to(old_password.name, password_url(old_password)) if old_password.present?
|
||||
end
|
||||
|
||||
prop
|
||||
end
|
||||
end
|
|
@ -1,28 +1,37 @@
|
|||
module AdditionalsMenuHelper
|
||||
def additionals_top_menu_setup
|
||||
return unless User.current.try(:hrm_user_type_id).nil?
|
||||
return if Redmine::Plugin.installed? 'redmine_hrm'
|
||||
|
||||
if Additionals.setting?(:remove_mypage)
|
||||
if Additionals.setting? :remove_mypage
|
||||
Redmine::MenuManager.map(:top_menu).delete(:my_page) if Redmine::MenuManager.map(:top_menu).exists?(:my_page)
|
||||
else
|
||||
handle_top_menu_item(:my_page, url: my_page_path, after: :home, if: proc { User.current.logged? })
|
||||
handle_top_menu_item(:my_page, { url: my_page_path, after: :home, if: proc { User.current.logged? } })
|
||||
end
|
||||
|
||||
if Additionals.setting?(:remove_help)
|
||||
if Additionals.setting? :remove_help
|
||||
Redmine::MenuManager.map(:top_menu).delete(:help) if Redmine::MenuManager.map(:top_menu).exists?(:help)
|
||||
elsif User.current.logged?
|
||||
handle_top_menu_item(:help, url: '#', symbol: 'fas_question', last: true)
|
||||
handle_top_submenu_item :help, url: '#', symbol: 'fas_question', last: true
|
||||
@additionals_help_items = additionals_help_menu_items
|
||||
else
|
||||
handle_top_menu_item(:help, url: Redmine::Info.help_url, symbol: 'fas_question', last: true)
|
||||
handle_top_menu_item :help, url: Redmine::Info.help_url, symbol: 'fas_question', last: true
|
||||
end
|
||||
end
|
||||
|
||||
def handle_top_menu_item(menu_name, item)
|
||||
def handle_top_submenu_item(menu_name, item)
|
||||
handle_top_menu_item menu_name, item, with_submenu: true
|
||||
end
|
||||
|
||||
def handle_top_menu_item(menu_name, item, with_submenu: false)
|
||||
Redmine::MenuManager.map(:top_menu).delete(menu_name.to_sym) if Redmine::MenuManager.map(:top_menu).exists?(menu_name.to_sym)
|
||||
|
||||
html_options = {}
|
||||
html_options[:class] = 'external' if item[:url].include? '://'
|
||||
|
||||
css_classes = []
|
||||
css_classes << 'top-submenu' if with_submenu
|
||||
css_classes << 'external' if item[:url].include? '://'
|
||||
html_options[:class] = css_classes.join(' ') if css_classes.present?
|
||||
|
||||
html_options[:title] = item[:title] if item[:title].present?
|
||||
|
||||
menu_options = { parent: item[:parent].present? ? item[:parent].to_sym : nil,
|
||||
|
@ -48,7 +57,7 @@ module AdditionalsMenuHelper
|
|||
menu_options[:before] = :help
|
||||
end
|
||||
|
||||
Redmine::MenuManager.map(:top_menu).push(menu_name, item[:url], menu_options)
|
||||
Redmine::MenuManager.map(:top_menu).push menu_name, item[:url], menu_options
|
||||
end
|
||||
|
||||
def render_custom_top_menu_item
|
||||
|
@ -56,14 +65,15 @@ module AdditionalsMenuHelper
|
|||
return if items.empty?
|
||||
|
||||
user_roles = Role.givable
|
||||
.joins(:members).where(members: { user_id: User.current.id })
|
||||
.joins(members: :project).where(projects: { status: Project::STATUS_ACTIVE })
|
||||
.joins(members: :project)
|
||||
.where(members: { user_id: User.current.id },
|
||||
projects: { status: Project::STATUS_ACTIVE })
|
||||
.distinct
|
||||
.reorder(nil)
|
||||
.pluck(:id)
|
||||
.ids
|
||||
|
||||
items.each do |item|
|
||||
additionals_custom_top_menu_item(item, user_roles)
|
||||
additionals_custom_top_menu_item item, user_roles
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -72,10 +82,10 @@ module AdditionalsMenuHelper
|
|||
Additionals::MAX_CUSTOM_MENU_ITEMS.times do |num|
|
||||
menu_name = "custom_menu#{num}"
|
||||
item = { menu_name: menu_name.to_sym,
|
||||
url: Additionals.settings[menu_name + '_url'],
|
||||
name: Additionals.settings[menu_name + '_name'],
|
||||
title: Additionals.settings[menu_name + '_title'],
|
||||
roles: Additionals.settings[menu_name + '_roles'] }
|
||||
url: Additionals.setting("#{menu_name}_url"),
|
||||
name: Additionals.setting("#{menu_name}_name"),
|
||||
title: Additionals.setting("#{menu_name}_title"),
|
||||
roles: Additionals.setting("#{menu_name}_roles") }
|
||||
|
||||
if item[:name].present? && item[:url].present? && item[:roles].present?
|
||||
items << item
|
||||
|
@ -110,7 +120,7 @@ module AdditionalsMenuHelper
|
|||
end
|
||||
|
||||
if show_entry
|
||||
handle_top_menu_item(item[:menu_name], item)
|
||||
handle_top_menu_item item[:menu_name], item
|
||||
elsif Redmine::MenuManager.map(:top_menu).exists?(item[:menu_name])
|
||||
Redmine::MenuManager.map(:top_menu).delete(item[:menu_name])
|
||||
end
|
||||
|
@ -118,12 +128,16 @@ module AdditionalsMenuHelper
|
|||
|
||||
def addtionals_help_plugin_items
|
||||
user_items = [{ title: 'Redmine Guide', url: Redmine::Info.help_url },
|
||||
{ title: "Redmine #{l(:label_macro_plural)}", url: additionals_macros_path }]
|
||||
{ title: "Redmine #{l :label_macro_plural}", url: additionals_macros_path }]
|
||||
|
||||
admin_items = [{ title: 'Additionals', url: 'https://additionals.readthedocs.io/en/latest/manual/', manual: true },
|
||||
{ title: 'Redmine Changelog', url: 'https://www.redmine.org/projects/redmine/wiki/Changelog_3_4' },
|
||||
{ title: 'Redmine Upgrade', url: 'https://www.redmine.org/projects/redmine/wiki/RedmineUpgrade' },
|
||||
{ title: 'Redmine Security Advisories', url: 'https://www.redmine.org/projects/redmine/wiki/Security_Advisories' }]
|
||||
admin_items = [{ title: 'Additionals',
|
||||
url: 'https://additionals.readthedocs.io/en/latest/manual/', manual: true },
|
||||
{ title: 'Redmine Changelog',
|
||||
url: "https://www.redmine.org/projects/redmine/wiki/Changelog_#{Redmine::VERSION::MAJOR}_#{Redmine::VERSION::MINOR}" },
|
||||
{ title: 'Redmine Upgrade',
|
||||
url: 'https://www.redmine.org/projects/redmine/wiki/RedmineUpgrade' },
|
||||
{ title: 'Redmine Security Advisories',
|
||||
url: 'https://www.redmine.org/projects/redmine/wiki/Security_Advisories' }]
|
||||
|
||||
Redmine::Plugin.all.each do |plugin|
|
||||
next if plugin.id == :additionals
|
||||
|
@ -145,7 +159,7 @@ module AdditionalsMenuHelper
|
|||
|
||||
plugin_item.each do |temp_item|
|
||||
u_items = if !temp_item[:manual].nil? && temp_item[:manual]
|
||||
{ title: "#{temp_item[:title]} #{l(:label_help_manual)}", url: temp_item[:url] }
|
||||
{ title: "#{temp_item[:title]} #{l :label_help_manual}", url: temp_item[:url] }
|
||||
else
|
||||
{ title: temp_item[:title], url: temp_item[:url] }
|
||||
end
|
||||
|
@ -173,18 +187,17 @@ module AdditionalsMenuHelper
|
|||
s = []
|
||||
pages.each_with_index do |item, idx|
|
||||
s << if item[:title] == '-'
|
||||
content_tag(:li, tag(:hr))
|
||||
tag.li tag.hr
|
||||
else
|
||||
html_options = { class: 'help_item_' + idx.to_s }
|
||||
html_options = { class: "help_item_#{idx}" }
|
||||
if item[:url].include? '://'
|
||||
html_options[:class] << ' external'
|
||||
html_options[:target] = '_blank'
|
||||
end
|
||||
content_tag(:li,
|
||||
link_to(item[:title], item[:url], html_options))
|
||||
tag.li(link_to(item[:title], item[:url], html_options))
|
||||
end
|
||||
end
|
||||
safe_join(s)
|
||||
safe_join s
|
||||
end
|
||||
|
||||
# Plugin help items definition for plugins,
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
module AdditionalsProjectsHelper
|
||||
def project_overview_name(_project, dashboard = nil)
|
||||
name = [l(:label_overview)]
|
||||
name << dashboard.name if dashboard.present? && (dashboard.always_expose? || !dashboard.system_default)
|
||||
|
||||
safe_join name, Additionals::LIST_SEPARATOR
|
||||
end
|
||||
end
|
|
@ -52,9 +52,9 @@ module AdditionalsQueriesHelper
|
|||
end
|
||||
|
||||
def additionals_load_query_id(query_class, session_key, query_id, options, object_type)
|
||||
cond = 'project_id IS NULL'
|
||||
cond << " OR project_id = #{@project.id}" if @project
|
||||
@query = query_class.where(cond).find(query_id)
|
||||
scope = query_class.where(project_id: nil)
|
||||
scope = scope.or(query_class.where(project_id: @project.id)) if @project
|
||||
@query = scope.find(query_id)
|
||||
raise ::Unauthorized unless @query.visible?
|
||||
|
||||
@query.project = @project
|
||||
|
@ -72,19 +72,20 @@ module AdditionalsQueriesHelper
|
|||
end
|
||||
|
||||
def additionals_query_cache_key(object_type)
|
||||
project_id = @project.nil? ? 0 : @project.id
|
||||
project_id = @project ? @project.id : 0
|
||||
"#{object_type}_query_data_#{session.id}_#{project_id}"
|
||||
end
|
||||
|
||||
def additionals_select2_search_users(where_filter = '', where_params = {})
|
||||
def additionals_select2_search_users(options = {})
|
||||
q = params[:q].to_s.strip
|
||||
exclude_id = params[:user_id].to_i
|
||||
scope = User.active.where(type: 'User')
|
||||
scope = scope.where.not(id: exclude_id) if exclude_id > 0
|
||||
scope = scope.where(where_filter, where_params) if where_filter.present?
|
||||
scope = scope.like(q) if q.present?
|
||||
scope = scope.visible unless options[:all_visible]
|
||||
scope = scope.where.not(id: exclude_id) if exclude_id.positive?
|
||||
scope = scope.where(options[:where_filter], options[:where_params]) if options[:where_filter]
|
||||
q.split(' ').map { |search_string| scope = scope.like(search_string) } if q.present?
|
||||
scope = scope.order(last_login_on: :desc)
|
||||
.limit(params[:limit] || Additionals::SELECT2_INIT_ENTRIES)
|
||||
.limit(Additionals::SELECT2_INIT_ENTRIES)
|
||||
@users = scope.to_a.sort! { |x, y| x.name <=> y.name }
|
||||
render layout: false, partial: 'auto_completes/additionals_users'
|
||||
end
|
||||
|
@ -97,9 +98,10 @@ module AdditionalsQueriesHelper
|
|||
query.columns
|
||||
end
|
||||
|
||||
stream = StringIO.new('')
|
||||
export_to_xlsx(items, columns, filename: stream)
|
||||
stream.string
|
||||
options[:filename] = StringIO.new('')
|
||||
|
||||
export_to_xlsx(items, columns, options)
|
||||
options[:filename].string
|
||||
end
|
||||
|
||||
def additionals_result_to_xlsx(items, columns, options = {})
|
||||
|
@ -161,10 +163,18 @@ module AdditionalsQueriesHelper
|
|||
columns.each_with_index do |c, column_index|
|
||||
value = csv_content(c, line)
|
||||
if c.name == :id # ID
|
||||
link = url_for(controller: line.class.name.underscore.pluralize, action: 'show', id: line.id)
|
||||
worksheet.write(line_index + 1, column_index, link, hyperlink_format, value)
|
||||
if options[:no_id_link].blank?
|
||||
link = url_for(controller: line.class.name.underscore.pluralize, action: 'show', id: line.id)
|
||||
worksheet.write(line_index + 1, column_index, link, hyperlink_format, value)
|
||||
else
|
||||
# id without link
|
||||
worksheet.write(line_index + 1,
|
||||
column_index,
|
||||
value,
|
||||
workbook.add_format(xlsx_cell_format(:cell, value, line_index)))
|
||||
end
|
||||
elsif xlsx_hyperlink_cell?(value)
|
||||
worksheet.write(line_index + 1, column_index, value, hyperlink_format, value)
|
||||
worksheet.write(line_index + 1, column_index, value[0..254], hyperlink_format, value)
|
||||
elsif !c.inline?
|
||||
# block column can be multiline strings
|
||||
value.gsub!("\r\n", "\n")
|
||||
|
@ -188,7 +198,7 @@ module AdditionalsQueriesHelper
|
|||
value_str = value.to_s
|
||||
|
||||
# 1.1: margin
|
||||
width = (value_str.length + value_str.chars.reject(&:ascii_only?).length) * 1.1 + 1
|
||||
width = (value_str.length + value_str.chars.count { |e| !e.ascii_only? }) * 1.1 + 1
|
||||
# 30: max width
|
||||
width > 30 ? 30 : width
|
||||
end
|
||||
|
@ -206,7 +216,7 @@ module AdditionalsQueriesHelper
|
|||
format[:bg_color] = 'silver' unless index.even?
|
||||
else
|
||||
format[:bg_color] = 'silver' unless index.even?
|
||||
format[:color] = 'red' if value.is_a?(Numeric) && value < 0
|
||||
format[:color] = 'red' if value.is_a?(Numeric) && value.negative?
|
||||
end
|
||||
|
||||
format
|
||||
|
@ -214,13 +224,13 @@ module AdditionalsQueriesHelper
|
|||
|
||||
def xlsx_hyperlink_cell?(token)
|
||||
# Match http, https or ftp URL
|
||||
if token =~ %r{\A[fh]tt?ps?://}
|
||||
if %r{\A[fh]tt?ps?://}.match?(token)
|
||||
true
|
||||
# Match mailto:
|
||||
elsif token.present? && token.start_with?('mailto:')
|
||||
true
|
||||
# Match internal or external sheet link
|
||||
elsif token =~ /\A(?:in|ex)ternal:/
|
||||
elsif /\A(?:in|ex)ternal:/.match?(token)
|
||||
true
|
||||
end
|
||||
end
|
||||
|
@ -229,7 +239,7 @@ module AdditionalsQueriesHelper
|
|||
# columns in ignored_column_names are skipped (names as symbols)
|
||||
# TODO: this is a temporary fix and should be removed
|
||||
# after https://www.redmine.org/issues/29830 is in Redmine core.
|
||||
def query_as_hidden_field_tags(query, ignored_column_names = nil)
|
||||
def query_as_hidden_field_tags(query)
|
||||
tags = hidden_field_tag('set_filter', '1', id: nil)
|
||||
|
||||
if query.filters.present?
|
||||
|
@ -243,8 +253,10 @@ module AdditionalsQueriesHelper
|
|||
else
|
||||
tags << hidden_field_tag('f[]', '', id: nil)
|
||||
end
|
||||
|
||||
ignored_block_columns = query.block_columns.map(&:name)
|
||||
query.columns.each do |column|
|
||||
next if ignored_column_names.present? && ignored_column_names.include?(column.name)
|
||||
next if ignored_block_columns.include?(column.name)
|
||||
|
||||
tags << hidden_field_tag('c[]', column.name, id: nil)
|
||||
end
|
||||
|
@ -258,4 +270,11 @@ module AdditionalsQueriesHelper
|
|||
|
||||
tags
|
||||
end
|
||||
|
||||
def render_query_group_view(query, locals = {})
|
||||
return if locals[:group_name].blank?
|
||||
|
||||
render partial: 'queries/additionals_group_view',
|
||||
locals: { query: query }.merge(locals)
|
||||
end
|
||||
end
|
||||
|
|
82
plugins/additionals/app/helpers/additionals_routes_helper.rb
Normal file
82
plugins/additionals/app/helpers/additionals_routes_helper.rb
Normal file
|
@ -0,0 +1,82 @@
|
|||
module AdditionalsRoutesHelper
|
||||
def _dashboards_path(project, *args)
|
||||
if project
|
||||
project_dashboards_path(project, *args)
|
||||
else
|
||||
dashboards_path(*args)
|
||||
end
|
||||
end
|
||||
|
||||
def _dashboard_path(project, *args)
|
||||
if project
|
||||
project_dashboard_path(project, *args)
|
||||
else
|
||||
dashboard_path(*args)
|
||||
end
|
||||
end
|
||||
|
||||
def _dashboard_async_blocks_path(project, *args)
|
||||
if project
|
||||
project_dashboard_async_blocks_path(project, *args)
|
||||
else
|
||||
dashboard_async_blocks_path(*args)
|
||||
end
|
||||
end
|
||||
|
||||
def _edit_dashboard_path(project, *args)
|
||||
if project
|
||||
edit_project_dashboard_path(project, *args)
|
||||
else
|
||||
edit_dashboard_path(*args)
|
||||
end
|
||||
end
|
||||
|
||||
def _new_dashboard_path(project, *args)
|
||||
if project
|
||||
new_project_dashboard_path(project, *args)
|
||||
else
|
||||
new_dashboard_path(*args)
|
||||
end
|
||||
end
|
||||
|
||||
def _update_layout_setting_dashboard_path(project, *args)
|
||||
if project
|
||||
update_layout_setting_project_dashboard_path(project, *args)
|
||||
else
|
||||
update_layout_setting_dashboard_path(*args)
|
||||
end
|
||||
end
|
||||
|
||||
def _add_block_dashboard_path(project, *args)
|
||||
if project
|
||||
add_block_project_dashboard_path(project, *args)
|
||||
else
|
||||
add_block_dashboard_path(*args)
|
||||
end
|
||||
end
|
||||
|
||||
def _remove_block_dashboard_path(project, *args)
|
||||
if project
|
||||
remove_block_project_dashboard_path(project, *args)
|
||||
else
|
||||
remove_block_dashboard_path(*args)
|
||||
end
|
||||
end
|
||||
|
||||
def _order_blocks_dashboard_path(project, *args)
|
||||
if project
|
||||
order_blocks_project_dashboard_path(project, *args)
|
||||
else
|
||||
order_blocks_dashboard_path(*args)
|
||||
end
|
||||
end
|
||||
|
||||
def dashboard_link_path(project, dashboard, options = {})
|
||||
options[:dashboard_id] = dashboard.id
|
||||
if dashboard.dashboard_type == DashboardContentProject::TYPE_NAME
|
||||
project_path project, options
|
||||
else
|
||||
home_path options
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,13 @@
|
|||
module AdditionalsSelect2Helper
|
||||
def additionals_select2_tag(name, option_tags = nil, options = {})
|
||||
s = select_tag(name, option_tags, options)
|
||||
s << hidden_field_tag("#{name}[]", '') if options[:multiple] && options.fetch(:include_hidden, true)
|
||||
|
||||
s + javascript_tag("select2Tag('#{sanitize_to_id name}', #{options.to_json});")
|
||||
end
|
||||
|
||||
# Transforms select filters of +type+ fields into select2
|
||||
def additionals_transform_to_select2(type, options = {})
|
||||
javascript_tag("setSelect2Filter('#{type}', #{options.to_json});") unless type.empty?
|
||||
end
|
||||
end
|
|
@ -0,0 +1,59 @@
|
|||
module AdditionalsSettingsHelper
|
||||
def additionals_settings_tabs
|
||||
tabs = [{ name: 'general', partial: 'additionals/settings/general', label: :label_general },
|
||||
{ name: 'wiki', partial: 'additionals/settings/wiki', label: :label_wiki },
|
||||
{ name: 'macros', partial: 'additionals/settings/macros', label: :label_macro_plural },
|
||||
{ name: 'rules', partial: 'additionals/settings/issues', label: :label_issue_plural },
|
||||
{ name: 'users', partial: 'additionals/settings/users', label: :label_user_plural },
|
||||
{ name: 'web', partial: 'additionals/settings/web_apis', label: :label_web_apis }]
|
||||
|
||||
unless Redmine::Plugin.installed? 'redmine_hrm'
|
||||
tabs << { name: 'menu', partial: 'additionals/settings/menu', label: :label_settings_menu }
|
||||
end
|
||||
|
||||
tabs
|
||||
end
|
||||
|
||||
def additionals_settings_checkbox(name, options = {})
|
||||
label_title = options.delete(:label).presence || l("label_#{name}")
|
||||
value = options.delete :value
|
||||
value_is_bool = options.delete :value_is_bool
|
||||
custom_value = if value.nil?
|
||||
value = 1
|
||||
false
|
||||
else
|
||||
value = 1 if value_is_bool
|
||||
true
|
||||
end
|
||||
|
||||
checked = if custom_value && !value_is_bool
|
||||
@settings[name]
|
||||
else
|
||||
Additionals.true? @settings[name]
|
||||
end
|
||||
|
||||
s = [label_tag("settings[#{name}]", label_title)]
|
||||
s << hidden_field_tag("settings[#{name}]", 0, id: nil) if !custom_value || value_is_bool
|
||||
s << check_box_tag("settings[#{name}]", value, checked, options)
|
||||
safe_join s
|
||||
end
|
||||
|
||||
def additionals_settings_textfield(name, options = {})
|
||||
label_title = options.delete(:label).presence || l("label_#{name}")
|
||||
value = options.delete(:value).presence || @settings[name]
|
||||
|
||||
safe_join [label_tag("settings[#{name}]", label_title),
|
||||
text_field_tag("settings[#{name}]", value, options)]
|
||||
end
|
||||
|
||||
def additionals_settings_textarea(name, options = {})
|
||||
label_title = options.delete(:label).presence || l("label_#{name}")
|
||||
value = options.delete(:value).presence || @settings[name]
|
||||
|
||||
options[:class] = 'wiki-edit' unless options.key?(:class)
|
||||
options[:rows] = addtionals_textarea_cols(value) unless options.key?(:rows)
|
||||
|
||||
safe_join [label_tag("settings[#{name}]", label_title),
|
||||
text_area_tag("settings[#{name}]", value, options)]
|
||||
end
|
||||
end
|
|
@ -1,16 +1,6 @@
|
|||
require 'digest/md5'
|
||||
|
||||
module AdditionalsTagHelper
|
||||
# deprecated: this will removed after a while
|
||||
def render_additionals_tags_list(tags, options = {})
|
||||
additionals_tag_cloud(tags, options)
|
||||
end
|
||||
|
||||
# deprecated: this will removed after a while
|
||||
def render_additionals_tag_link_line(tag_list)
|
||||
additionals_tag_links(tag_list)
|
||||
end
|
||||
|
||||
def additionals_tag_cloud(tags, options = {})
|
||||
return if tags.blank?
|
||||
|
||||
|
@ -31,7 +21,7 @@ module AdditionalsTagHelper
|
|||
' '
|
||||
end
|
||||
|
||||
content_tag(:div, safe_join(s, sep), class: 'tags')
|
||||
tag.div safe_join(s, sep), class: 'tags'
|
||||
end
|
||||
|
||||
# plain list of tags
|
||||
|
@ -54,33 +44,31 @@ module AdditionalsTagHelper
|
|||
end
|
||||
|
||||
safe_join(tag_list.map do |tag|
|
||||
additionals_tag_link(tag, options)
|
||||
additionals_tag_link tag, options
|
||||
end, sep)
|
||||
end
|
||||
|
||||
def additionals_tag_link(tag, options = {})
|
||||
def additionals_tag_link(tag_object, options = {})
|
||||
tag_name = []
|
||||
tag_name << tag.name
|
||||
tag_name << tag_object.name
|
||||
|
||||
unless options[:tags_without_color]
|
||||
tag_bg_color = additionals_tag_color(tag.name)
|
||||
tag_fg_color = additionals_tag_fg_color(tag_bg_color)
|
||||
tag_bg_color = additionals_tag_color tag_object.name
|
||||
tag_fg_color = additionals_tag_fg_color tag_bg_color
|
||||
tag_style = "background-color: #{tag_bg_color}; color: #{tag_fg_color}"
|
||||
end
|
||||
|
||||
tag_name << content_tag('span', "(#{tag.count})", class: 'tag-count') if options[:show_count]
|
||||
tag_name << tag.span("(#{tag_object.count})", class: 'tag-count') if options[:show_count]
|
||||
|
||||
if options[:tags_without_color]
|
||||
content_tag('span',
|
||||
link_to(safe_join(tag_name), additionals_tag_url(tag.name, options)),
|
||||
class: 'tag-label')
|
||||
tag.span link_to(safe_join(tag_name), additionals_tag_url(tag_object.name, options)),
|
||||
class: 'tag-label'
|
||||
else
|
||||
content_tag('span',
|
||||
link_to(safe_join(tag_name),
|
||||
additionals_tag_url(tag.name, options),
|
||||
style: tag_style),
|
||||
class: 'additionals-tag-label-color',
|
||||
style: tag_style)
|
||||
tag.span link_to(safe_join(tag_name),
|
||||
additionals_tag_url(tag_object.name, options),
|
||||
style: tag_style),
|
||||
class: 'additionals-tag-label-color',
|
||||
style: tag_style
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -90,7 +78,7 @@ module AdditionalsTagHelper
|
|||
{ controller: options[:tag_controller].presence || controller_name,
|
||||
action: action,
|
||||
set_filter: 1,
|
||||
project_id: @project,
|
||||
project_id: options[:project],
|
||||
fields: [:tags],
|
||||
values: { tags: [tag_name] },
|
||||
operators: { tags: '=' } }
|
||||
|
|
459
plugins/additionals/app/helpers/dashboards_helper.rb
Normal file
459
plugins/additionals/app/helpers/dashboards_helper.rb
Normal file
|
@ -0,0 +1,459 @@
|
|||
module DashboardsHelper
|
||||
def dashboard_sidebar?(dashboard, params)
|
||||
if params['enable_sidebar'].blank?
|
||||
if dashboard.blank?
|
||||
# defaults without dashboard
|
||||
!@project.nil?
|
||||
else
|
||||
dashboard.enable_sidebar?
|
||||
end
|
||||
else
|
||||
Additionals.true? params['enable_sidebar']
|
||||
end
|
||||
end
|
||||
|
||||
def welcome_overview_name(dashboard = nil)
|
||||
name = [l(:label_home)]
|
||||
name << dashboard.name if dashboard&.always_expose? || dashboard.present? && !dashboard.system_default?
|
||||
|
||||
safe_join name, Additionals::LIST_SEPARATOR
|
||||
end
|
||||
|
||||
def dashboard_css_classes(dashboard)
|
||||
classes = ['dashboard', dashboard.dashboard_type.underscore, "dashboard-#{dashboard.id}"]
|
||||
safe_join classes, ' '
|
||||
end
|
||||
|
||||
def sidebar_dashboards(dashboard, project = nil, user = nil)
|
||||
user ||= User.current
|
||||
scope = Dashboard.visible.includes([:author])
|
||||
|
||||
scope = if project.present?
|
||||
scope = scope.project_only
|
||||
scope.where(project_id: project.id)
|
||||
.or(scope.where(system_default: true)
|
||||
.where(project_id: nil))
|
||||
.or(scope.where(author_id: user.id)
|
||||
.where(project_id: nil))
|
||||
else
|
||||
scope.where dashboard_type: dashboard.dashboard_type
|
||||
end
|
||||
|
||||
scope.sorted.to_a
|
||||
end
|
||||
|
||||
def render_dashboard_actionlist(active_dashboard, project = nil)
|
||||
dashboards = sidebar_dashboards active_dashboard, project
|
||||
base_css = 'icon icon-dashboard'
|
||||
out = []
|
||||
|
||||
dashboards.select!(&:public?) unless User.current.allowed_to? :save_dashboards, project, global: true
|
||||
dashboards.each do |dashboard|
|
||||
css_class = base_css
|
||||
dashboard_name = "#{l :label_dashboard}: #{dashboard.name}"
|
||||
out << if dashboard.id == active_dashboard.id
|
||||
link_to dashboard_name, '#',
|
||||
onclick: 'return false;',
|
||||
class: "#{base_css} disabled"
|
||||
else
|
||||
dashboard_link dashboard, project,
|
||||
class: css_class,
|
||||
title: l(:label_change_to_dashboard),
|
||||
name: dashboard_name
|
||||
end
|
||||
end
|
||||
|
||||
safe_join out
|
||||
end
|
||||
|
||||
def render_sidebar_dashboards(dashboard, project = nil)
|
||||
dashboards = sidebar_dashboards dashboard, project
|
||||
out = [dashboard_links(l(:label_my_dashboard_plural),
|
||||
dashboard,
|
||||
User.current.allowed_to?(:save_dashboards, project, global: true) ? dashboards.select(&:private?) : [],
|
||||
project),
|
||||
dashboard_links(l(:label_shared_dashboard_plural),
|
||||
dashboard,
|
||||
dashboards.select(&:public?),
|
||||
project)]
|
||||
|
||||
out << dashboard_info(dashboard) if dashboard.always_expose? || !dashboard.system_default
|
||||
|
||||
safe_join out
|
||||
end
|
||||
|
||||
def dashboard_info(dashboard)
|
||||
tag.div class: 'active-dashboards' do
|
||||
out = [tag.h3(l(:label_active_dashboard)),
|
||||
tag.ul do
|
||||
concat tag.ul "#{l :field_name}: #{dashboard.name}"
|
||||
concat tag.ul safe_join([l(:field_author), link_to_user(dashboard.author)], ': ')
|
||||
concat tag.ul "#{l :field_created_on}: #{format_time dashboard.created_at}"
|
||||
concat tag.ul "#{l :field_updated_on}: #{format_time dashboard.updated_at}"
|
||||
end]
|
||||
|
||||
if dashboard.description.present?
|
||||
out << tag.div(textilizable(dashboard, :description, inline_attachments: false),
|
||||
class: 'dashboard-description')
|
||||
end
|
||||
|
||||
safe_join out
|
||||
end
|
||||
end
|
||||
|
||||
def dashboard_links(title, active_dashboard, dashboards, project)
|
||||
return '' unless dashboards.any?
|
||||
|
||||
tag.h3(title, class: 'dashboards') +
|
||||
tag.ul do
|
||||
dashboards.each do |dashboard|
|
||||
selected = dashboard.id == if params[:dashboard_id].present?
|
||||
params[:dashboard_id].to_i
|
||||
else
|
||||
active_dashboard.id
|
||||
end
|
||||
|
||||
css = 'dashboard'
|
||||
css << ' selected' if selected
|
||||
link = [dashboard_link(dashboard, project, class: css)]
|
||||
if dashboard.system_default?
|
||||
link << if dashboard.project_id.nil?
|
||||
font_awesome_icon('fas_cube',
|
||||
title: l(:field_system_default),
|
||||
class: 'dashboard-system-default global')
|
||||
else
|
||||
font_awesome_icon('fas_cube',
|
||||
title: l(:field_project_system_default),
|
||||
class: 'dashboard-system-default project')
|
||||
end
|
||||
end
|
||||
concat tag.li safe_join(link)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def dashboard_link(dashboard, project, options = {})
|
||||
if options[:title].blank? && dashboard.public?
|
||||
author = if dashboard.author_id == User.current.id
|
||||
l :label_me
|
||||
else
|
||||
dashboard.author
|
||||
end
|
||||
options[:title] = l(:label_dashboard_author, name: author)
|
||||
end
|
||||
|
||||
name = options.delete(:name) || dashboard.name
|
||||
link_to name, dashboard_link_path(project, dashboard), options
|
||||
end
|
||||
|
||||
def sidebar_action_toggle(enabled, dashboard, project = nil)
|
||||
return if dashboard.nil?
|
||||
|
||||
if enabled
|
||||
link_to l(:label_disable_sidebar),
|
||||
dashboard_link_path(project, dashboard, enable_sidebar: 0),
|
||||
class: 'icon icon-sidebar'
|
||||
else
|
||||
link_to l(:label_enable_sidebar),
|
||||
dashboard_link_path(project, dashboard, enable_sidebar: 1),
|
||||
class: 'icon icon-sidebar'
|
||||
end
|
||||
end
|
||||
|
||||
def delete_dashboard_link(url, options = {})
|
||||
options = { method: :delete,
|
||||
data: { confirm: l(:text_are_you_sure) },
|
||||
class: 'icon icon-del' }.merge(options)
|
||||
|
||||
link_to l(:button_dashboard_delete), url, options
|
||||
end
|
||||
|
||||
# Returns the select tag used to add or remove a block
|
||||
def dashboard_block_select_tag(dashboard)
|
||||
blocks_in_use = dashboard.layout.values.flatten
|
||||
options = tag.option "<< #{l :label_add_dashboard_block} >>", value: ''
|
||||
dashboard.content.block_options(blocks_in_use).each do |label, block|
|
||||
options << tag.option(label, value: block, disabled: block.blank?)
|
||||
end
|
||||
select_tag 'block',
|
||||
options,
|
||||
id: 'block-select',
|
||||
class: 'dashboard-block-select',
|
||||
onchange: "$('#block-form').submit();"
|
||||
end
|
||||
|
||||
# Renders the blocks
|
||||
def render_dashboard_blocks(blocks, dashboard, _options = {})
|
||||
s = ''.html_safe
|
||||
|
||||
if blocks.present?
|
||||
blocks.each do |block|
|
||||
s << render_dashboard_block(block, dashboard).to_s
|
||||
end
|
||||
end
|
||||
s
|
||||
end
|
||||
|
||||
# Renders a single block
|
||||
def render_dashboard_block(block, dashboard, overwritten_settings = {})
|
||||
block_definition = dashboard.content.find_block block
|
||||
unless block_definition
|
||||
Rails.logger.info "Unknown block \"#{block}\" found in #{dashboard.name} (id=#{dashboard.id})"
|
||||
return
|
||||
end
|
||||
|
||||
content = render_dashboard_block_content block, block_definition, dashboard, overwritten_settings
|
||||
return if content.blank?
|
||||
|
||||
if dashboard.editable?
|
||||
icons = []
|
||||
if block_definition[:no_settings].blank? &&
|
||||
(!block_definition.key?(:with_settings_if) || block_definition[:with_settings_if].call(@project))
|
||||
icons << link_to_function(l(:label_options),
|
||||
"$('##{block}-settings').toggle();",
|
||||
class: 'icon-only icon-settings',
|
||||
title: l(:label_options))
|
||||
end
|
||||
icons << tag.span('', class: 'icon-only icon-sort-handle sort-handle', title: l(:button_move))
|
||||
icons << link_to(l(:button_delete),
|
||||
_remove_block_dashboard_path(@project, dashboard, block: block),
|
||||
remote: true, method: 'post',
|
||||
class: 'icon-only icon-close', title: l(:button_delete))
|
||||
|
||||
content = tag.div(safe_join(icons), class: 'contextual') + content
|
||||
end
|
||||
|
||||
tag.div content, class: 'mypage-box', id: "block-#{block}"
|
||||
end
|
||||
|
||||
def build_dashboard_partial_locals(block, block_definition, settings, dashboard)
|
||||
partial_locals = { dashboard: dashboard,
|
||||
settings: settings,
|
||||
block: block,
|
||||
block_definition: block_definition,
|
||||
user: User.current }
|
||||
|
||||
if block_definition[:query_block]
|
||||
partial_locals[:query_block] = block_definition[:query_block]
|
||||
partial_locals[:klass] = block_definition[:query_block][:class]
|
||||
partial_locals[:async] = { required_settings: %i[query_id],
|
||||
exposed_params: %i[sort],
|
||||
partial: 'dashboards/blocks/query_list' }
|
||||
partial_locals[:async][:unique_params] = [Redmine::Utils.random_hex(16)] if params[:refresh].present?
|
||||
partial_locals[:async] = partial_locals[:async].merge(block_definition[:async]) if block_definition[:async]
|
||||
elsif block_definition[:async]
|
||||
partial_locals[:async] = block_definition[:async]
|
||||
end
|
||||
|
||||
partial_locals
|
||||
end
|
||||
|
||||
def dashboard_async_required_settings?(settings, async)
|
||||
return true if async[:required_settings].blank?
|
||||
return false if settings.blank?
|
||||
|
||||
async[:required_settings].each do |required_setting|
|
||||
return false if settings.exclude?(required_setting) || settings[required_setting].blank?
|
||||
end
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
def dashboard_query_list_block_title(query, query_block, project)
|
||||
title = []
|
||||
title << query.project if project.nil? && query.project
|
||||
title << query_block[:label]
|
||||
|
||||
title << if query_block[:with_project]
|
||||
link_to(query.name, send(query_block[:link_helper], project, query.as_params))
|
||||
else
|
||||
link_to(query.name, send(query_block[:link_helper], query.as_params))
|
||||
end
|
||||
|
||||
safe_join title, Additionals::LIST_SEPARATOR
|
||||
end
|
||||
|
||||
def dashboard_query_list_block_alerts(dashboard, query, block_definition)
|
||||
return if dashboard.visibility == Dashboard::VISIBILITY_PRIVATE
|
||||
|
||||
title = if query.visibility == Query::VISIBILITY_PRIVATE
|
||||
l(:alert_only_visible_by_yourself)
|
||||
elsif block_definition.key?(:admin_only) && block_definition[:admin_only]
|
||||
l(:alert_only_visible_by_admins)
|
||||
end
|
||||
|
||||
return if title.nil?
|
||||
|
||||
font_awesome_icon('fas_info-circle',
|
||||
title: title,
|
||||
class: 'dashboard-block-alert')
|
||||
end
|
||||
|
||||
def render_legacy_left_block(_block, _block_definition, _settings, _dashboard)
|
||||
if @project
|
||||
call_hook :view_projects_show_left, project: @project
|
||||
else
|
||||
call_hook :view_welcome_index_left
|
||||
end
|
||||
end
|
||||
|
||||
def render_legacy_right_block(_block, _block_definition, _settings, _dashboard)
|
||||
if @project
|
||||
call_hook :view_projects_show_right, project: @project
|
||||
else
|
||||
call_hook :view_welcome_index_right
|
||||
end
|
||||
end
|
||||
|
||||
# copied from my_helper
|
||||
def render_documents_block(block, _block_definition, settings, dashboard)
|
||||
max_entries = settings[:max_entries] || DashboardContent::DEFAULT_MAX_ENTRIES
|
||||
|
||||
scope = Document.visible
|
||||
scope = scope.where(project: dashboard.project) if dashboard.project
|
||||
|
||||
documents = scope.order(created_on: :desc)
|
||||
.limit(max_entries)
|
||||
.to_a
|
||||
|
||||
render partial: 'dashboards/blocks/documents', locals: { block: block,
|
||||
max_entries: max_entries,
|
||||
documents: documents }
|
||||
end
|
||||
|
||||
def render_news_block(block, _block_definition, settings, dashboard)
|
||||
max_entries = settings[:max_entries] || DashboardContent::DEFAULT_MAX_ENTRIES
|
||||
|
||||
news = if dashboard.content_project.nil?
|
||||
News.latest User.current
|
||||
else
|
||||
dashboard.content_project
|
||||
.news
|
||||
.limit(max_entries)
|
||||
.includes(:author, :project)
|
||||
.reorder(created_on: :desc)
|
||||
.to_a
|
||||
end
|
||||
|
||||
render partial: 'dashboards/blocks/news', locals: { block: block,
|
||||
max_entries: max_entries,
|
||||
news: news }
|
||||
end
|
||||
|
||||
def render_my_spent_time_block(block, block_definition, settings, dashboard)
|
||||
days = settings[:days].to_i
|
||||
days = 7 if days < 1 || days > 365
|
||||
|
||||
scope = TimeEntry.where user_id: User.current.id
|
||||
scope = scope.where(project_id: dashboard.content_project.id) unless dashboard.content_project.nil?
|
||||
|
||||
entries_today = scope.where(spent_on: User.current.today)
|
||||
entries_days = scope.where(spent_on: User.current.today - (days - 1)..User.current.today)
|
||||
|
||||
render partial: 'dashboards/blocks/my_spent_time',
|
||||
locals: { block: block,
|
||||
block_definition: block_definition,
|
||||
entries_today: entries_today,
|
||||
entries_days: entries_days,
|
||||
days: days }
|
||||
end
|
||||
|
||||
def activity_dashboard_data(settings, dashboard)
|
||||
max_entries = (settings[:max_entries] || DashboardContent::DEFAULT_MAX_ENTRIES).to_i
|
||||
user = User.current
|
||||
options = {}
|
||||
options[:author] = user if Additionals.true? settings[:me_only]
|
||||
options[:project] = dashboard.content_project if dashboard.content_project.present?
|
||||
|
||||
Redmine::Activity::Fetcher.new(user, options)
|
||||
.events(nil, nil, limit: max_entries)
|
||||
.group_by { |event| user.time_to_date(event.event_datetime) }
|
||||
end
|
||||
|
||||
def dashboard_feed_catcher(url, max_entries)
|
||||
feed = { items: [], valid: false }
|
||||
return feed if url.blank?
|
||||
|
||||
cnt = 0
|
||||
max_entries = max_entries.present? ? max_entries.to_i : 10
|
||||
|
||||
begin
|
||||
URI.open(url) do |rss_feed|
|
||||
rss = RSS::Parser.parse(rss_feed)
|
||||
rss.items.each do |item|
|
||||
cnt += 1
|
||||
feed[:items] << { title: item.title.try(:content)&.presence || item.title,
|
||||
link: item.link.try(:href)&.presence || item.link }
|
||||
break if cnt >= max_entries
|
||||
end
|
||||
end
|
||||
rescue StandardError => e
|
||||
Rails.logger.info "dashboard_feed_catcher error for #{url}: #{e}"
|
||||
return feed
|
||||
end
|
||||
|
||||
feed[:valid] = true
|
||||
|
||||
feed
|
||||
end
|
||||
|
||||
def dashboard_feed_title(title, block_definition)
|
||||
title.presence || block_definition[:label]
|
||||
end
|
||||
|
||||
def options_for_query_select(klass, project)
|
||||
# sidebar_queries cannot be use because descendants classes are included
|
||||
# this changes on class loading
|
||||
# queries = klass.visible.global_or_on_project(@project).sorted.to_a
|
||||
queries = klass.visible
|
||||
.global_or_on_project(project)
|
||||
.where(type: klass.to_s)
|
||||
.sorted.to_a
|
||||
|
||||
tag.option + options_from_collection_for_select(queries, :id, :name)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Renders a single block content
|
||||
def render_dashboard_block_content(block, block_definition, dashboard, overwritten_settings = {})
|
||||
settings = dashboard.layout_settings block
|
||||
settings = settings.merge(overwritten_settings) if overwritten_settings.present?
|
||||
|
||||
partial = block_definition[:partial]
|
||||
partial_locals = build_dashboard_partial_locals block, block_definition, settings, dashboard
|
||||
|
||||
if block_definition[:query_block] || block_definition[:async]
|
||||
render partial: 'dashboards/blocks/async', locals: partial_locals
|
||||
elsif partial
|
||||
begin
|
||||
render partial: partial, locals: partial_locals
|
||||
rescue ActionView::MissingTemplate
|
||||
Rails.logger.warn("Partial \"#{partial}\" missing for block \"#{block}\" found in #{dashboard.name} (id=#{dashboard.id})")
|
||||
nil
|
||||
end
|
||||
else
|
||||
send "render_#{block_definition[:name]}_block",
|
||||
block,
|
||||
block_definition,
|
||||
settings,
|
||||
dashboard
|
||||
end
|
||||
end
|
||||
|
||||
def resently_used_dashboard_save(dashboard, project = nil)
|
||||
user = User.current
|
||||
dashboard_type = dashboard.dashboard_type
|
||||
recently_id = user.pref.recently_used_dashboard dashboard_type, project
|
||||
return if recently_id == dashboard.id || user.anonymous?
|
||||
|
||||
if dashboard_type == DashboardContentProject::TYPE_NAME
|
||||
user.pref.recently_used_dashboards[dashboard_type] = {} if user.pref.recently_used_dashboards[dashboard_type].nil?
|
||||
user.pref.recently_used_dashboards[dashboard_type][project.id] = dashboard.id
|
||||
else
|
||||
user.pref.recently_used_dashboards[dashboard_type] = dashboard.id
|
||||
end
|
||||
|
||||
user.pref.save
|
||||
end
|
||||
end
|
Loading…
Add table
Add a link
Reference in a new issue