Nuevo plugin Additionals 2.0.20
This commit is contained in:
parent
a2a901b71b
commit
93e1e28683
354 changed files with 40514 additions and 0 deletions
46
plugins/additionals/app/helpers/additionals_fontawesome_helper.rb
Executable file
46
plugins/additionals/app/helpers/additionals_fontawesome_helper.rb
Executable file
|
@ -0,0 +1,46 @@
|
|||
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
|
||||
# fab_font-awesome
|
||||
# options = class
|
||||
# pre_text
|
||||
# post_text
|
||||
# title
|
||||
def font_awesome_icon(name, options = {})
|
||||
info = AdditionalsFontAwesome.value_info(name)
|
||||
return '' if info.blank?
|
||||
|
||||
post_text = ''
|
||||
options['aria-hidden'] = 'true'
|
||||
options[:class] = if options[:class].present?
|
||||
info[:classes] + ' ' + options[:class]
|
||||
else
|
||||
info[:classes]
|
||||
end
|
||||
|
||||
s = []
|
||||
if options[:pre_text].present?
|
||||
s << options[:pre_text]
|
||||
s << ' '
|
||||
options.delete(:pre_text)
|
||||
end
|
||||
if options[:post_text].present?
|
||||
post_text = options[:post_text]
|
||||
options.delete(:post_text)
|
||||
end
|
||||
s << content_tag('span', '', options)
|
||||
if post_text.present?
|
||||
s << ' '
|
||||
s << post_text
|
||||
end
|
||||
safe_join(s)
|
||||
end
|
||||
end
|
24
plugins/additionals/app/helpers/additionals_issues_helper.rb
Executable file
24
plugins/additionals/app/helpers/additionals_issues_helper.rb
Executable file
|
@ -0,0 +1,24 @@
|
|||
module AdditionalsIssuesHelper
|
||||
def issue_author_options_for_select(project, issue = nil)
|
||||
authors = project.users.sorted
|
||||
s = []
|
||||
return s unless authors.any?
|
||||
|
||||
s << content_tag('option', "<< #{l(:label_me)} >>", value: User.current.id) if authors.include?(User.current)
|
||||
|
||||
if issue.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)
|
||||
end
|
||||
safe_join(s)
|
||||
end
|
||||
|
||||
def show_issue_change_author?(issue)
|
||||
if issue.new_record? && User.current.allowed_to?(:change_new_issue_author, issue.project) ||
|
||||
issue.persisted? && User.current.allowed_to?(:edit_issue_author, issue.project)
|
||||
true
|
||||
end
|
||||
end
|
||||
end
|
214
plugins/additionals/app/helpers/additionals_menu_helper.rb
Executable file
214
plugins/additionals/app/helpers/additionals_menu_helper.rb
Executable file
|
@ -0,0 +1,214 @@
|
|||
module AdditionalsMenuHelper
|
||||
def additionals_top_menu_setup
|
||||
return unless User.current.try(:hrm_user_type_id).nil?
|
||||
|
||||
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? })
|
||||
end
|
||||
|
||||
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)
|
||||
@additionals_help_items = additionals_help_menu_items
|
||||
else
|
||||
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)
|
||||
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? '://'
|
||||
html_options[:title] = item[:title] if item[:title].present?
|
||||
|
||||
menu_options = { parent: item[:parent].present? ? item[:parent].to_sym : nil,
|
||||
html: html_options }
|
||||
|
||||
menu_options[:if] = menu_options[:if] if menu_options[:if].present?
|
||||
|
||||
menu_options[:caption] = if item[:symbol].present? && item[:name].present?
|
||||
font_awesome_icon(item[:symbol], post_text: item[:name])
|
||||
elsif item[:symbol].present?
|
||||
font_awesome_icon(item[:symbol])
|
||||
elsif item[:name].present?
|
||||
item[:name].to_s
|
||||
end
|
||||
|
||||
if item[:last].present? && item[:last]
|
||||
menu_options[:last] = true
|
||||
elsif item[:before].present?
|
||||
menu_options[:before] = item[:before]
|
||||
elsif item[:after].present?
|
||||
menu_options[:after] = item[:after]
|
||||
else
|
||||
menu_options[:before] = :help
|
||||
end
|
||||
|
||||
Redmine::MenuManager.map(:top_menu).push(menu_name, item[:url], menu_options)
|
||||
end
|
||||
|
||||
def render_custom_top_menu_item
|
||||
items = additionals_build_custom_items
|
||||
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 })
|
||||
.distinct
|
||||
.reorder(nil)
|
||||
.pluck(:id)
|
||||
|
||||
items.each do |item|
|
||||
additionals_custom_top_menu_item(item, user_roles)
|
||||
end
|
||||
end
|
||||
|
||||
def additionals_build_custom_items
|
||||
items = []
|
||||
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'] }
|
||||
|
||||
if item[:name].present? && item[:url].present? && item[:roles].present?
|
||||
items << item
|
||||
elsif Redmine::MenuManager.map(:top_menu).exists?(item[:menu_name])
|
||||
Redmine::MenuManager.map(:top_menu).delete(item[:menu_name])
|
||||
end
|
||||
end
|
||||
|
||||
items
|
||||
end
|
||||
|
||||
def additionals_custom_top_menu_item(item, user_roles)
|
||||
show_entry = false
|
||||
item[:roles].each do |role|
|
||||
if user_roles.empty? && role.to_i == Role::BUILTIN_ANONYMOUS
|
||||
show_entry = true
|
||||
break
|
||||
elsif User.current.logged? && role.to_i == Role::BUILTIN_NON_MEMBER
|
||||
# if user is logged in and non_member is active in item,
|
||||
# always show it
|
||||
show_entry = true
|
||||
break
|
||||
end
|
||||
|
||||
user_roles.each do |user_role|
|
||||
if role.to_i == user_role
|
||||
show_entry = true
|
||||
break
|
||||
end
|
||||
end
|
||||
break if show_entry == true
|
||||
end
|
||||
|
||||
if show_entry
|
||||
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
|
||||
end
|
||||
|
||||
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 }]
|
||||
|
||||
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' }]
|
||||
|
||||
Redmine::Plugin.all.each do |plugin|
|
||||
next if plugin.id == :additionals
|
||||
|
||||
plugin_item_base = nil
|
||||
|
||||
begin
|
||||
plugin_item_base = plugin.id.to_s.camelize.constantize
|
||||
rescue LoadError
|
||||
Rails.logger.debug "Ignore plugin #{plugin.id} for help integration"
|
||||
rescue StandardError => e
|
||||
raise e unless e.class.to_s == 'NameError'
|
||||
end
|
||||
|
||||
plugin_item = plugin_item_base.try(:additionals_help_items) unless plugin_item_base.nil?
|
||||
plugin_item = additionals_help_items_fallbacks(plugin.id) if plugin_item.nil?
|
||||
|
||||
next if plugin_item.nil?
|
||||
|
||||
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] }
|
||||
else
|
||||
{ title: temp_item[:title], url: temp_item[:url] }
|
||||
end
|
||||
|
||||
if !temp_item[:admin].nil? && temp_item[:admin]
|
||||
admin_items << u_items
|
||||
else
|
||||
user_items << u_items
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
{ user: user_items, admin: admin_items }
|
||||
end
|
||||
|
||||
def additionals_help_menu_items
|
||||
plugin_items = addtionals_help_plugin_items
|
||||
pages = plugin_items[:user].sort_by { |k| k[:title] }
|
||||
|
||||
if User.current.admin?
|
||||
pages << { title: '-' }
|
||||
pages += plugin_items[:admin].sort_by { |k| k[:title] }
|
||||
end
|
||||
|
||||
s = []
|
||||
pages.each_with_index do |item, idx|
|
||||
s << if item[:title] == '-'
|
||||
content_tag(:li, tag(:hr))
|
||||
else
|
||||
html_options = { class: 'help_item_' + idx.to_s }
|
||||
if item[:url].include? '://'
|
||||
html_options[:class] << ' external'
|
||||
html_options[:target] = '_blank'
|
||||
end
|
||||
content_tag(:li,
|
||||
link_to(item[:title], item[:url], html_options))
|
||||
end
|
||||
end
|
||||
safe_join(s)
|
||||
end
|
||||
|
||||
# Plugin help items definition for plugins,
|
||||
# which do not have additionals_help_menu_items integration
|
||||
def additionals_help_items_fallbacks(plugin_id)
|
||||
plugins = { redmine_wiki_lists: [{ title: 'Wiki Lists Macros',
|
||||
url: 'https://www.r-labs.org/projects/wiki_lists/wiki/Wiki_Lists_en' }],
|
||||
redmine_wiki_extensions: [{ title: 'Wiki Extensions',
|
||||
url: 'https://www.r-labs.org/projects/r-labs/wiki/Wiki_Extensions_en' }],
|
||||
redmine_git_hosting: [{ title: 'Redmine Git Hosting',
|
||||
url: 'http://redmine-git-hosting.io/get_started/',
|
||||
admin: true }],
|
||||
redmine_contacts: [{ title: 'Redmine CRM',
|
||||
url: 'https://www.redmineup.com/pages/help/crm',
|
||||
admin: true }],
|
||||
redmine_contacts_helpdesk: [{ title: 'Redmine Helpdesk',
|
||||
url: 'https://www.redmineup.com/pages/help/helpdesk',
|
||||
admin: true }],
|
||||
redmine_ldap_sync: [{ title: 'Redmine LDAP',
|
||||
url: 'https://www.redmine.org/projects/redmine/wiki/RedmineLDAP',
|
||||
admin: true },
|
||||
{ title: 'Redmine LDAP Sync',
|
||||
url: 'https://github.com/thorin/redmine_ldap_sync/blob/master/README.md',
|
||||
admin: true }] }
|
||||
plugins[plugin_id]
|
||||
end
|
||||
end
|
261
plugins/additionals/app/helpers/additionals_queries_helper.rb
Executable file
261
plugins/additionals/app/helpers/additionals_queries_helper.rb
Executable file
|
@ -0,0 +1,261 @@
|
|||
module AdditionalsQueriesHelper
|
||||
def additionals_query_session_key(object_type)
|
||||
"#{object_type}_query".to_sym
|
||||
end
|
||||
|
||||
def additionals_retrieve_query(object_type, options = {})
|
||||
session_key = additionals_query_session_key(object_type)
|
||||
query_class = Object.const_get("#{object_type.camelcase}Query")
|
||||
if params[:query_id].present?
|
||||
additionals_load_query_id(query_class, session_key, params[:query_id], options, object_type)
|
||||
elsif api_request? ||
|
||||
params[:set_filter] ||
|
||||
session[session_key].nil? ||
|
||||
session[session_key][:project_id] != (@project ? @project.id : nil)
|
||||
# Give it a name, required to be valid
|
||||
@query = query_class.new(name: '_')
|
||||
@query.project = @project
|
||||
@query.user_filter = options[:user_filter] if options[:user_filter]
|
||||
@query.build_from_params(params)
|
||||
session[session_key] = { project_id: @query.project_id }
|
||||
# session has a limit to 4k, we have to use a cache for it for larger data
|
||||
Rails.cache.write(additionals_query_cache_key(object_type),
|
||||
filters: @query.filters,
|
||||
group_by: @query.group_by,
|
||||
column_names: @query.column_names,
|
||||
totalable_names: @query.totalable_names,
|
||||
sort_criteria: params[:sort].presence || @query.sort_criteria.to_a)
|
||||
else
|
||||
# retrieve from session
|
||||
@query = query_class.find_by(id: session[session_key][:id]) if session[session_key][:id]
|
||||
session_data = Rails.cache.read(additionals_query_cache_key(object_type))
|
||||
@query ||= query_class.new(name: '_',
|
||||
filters: session_data.nil? ? nil : session_data[:filters],
|
||||
group_by: session_data.nil? ? nil : session_data[:group_by],
|
||||
column_names: session_data.nil? ? nil : session_data[:column_names],
|
||||
totalable_names: session_data.nil? ? nil : session_data[:totalable_names],
|
||||
sort_criteria: params[:sort].presence || (session_data.nil? ? nil : session_data[:sort_criteria]))
|
||||
@query.project = @project
|
||||
if params[:sort].present?
|
||||
@query.sort_criteria = params[:sort]
|
||||
# we have to write cache for sort order
|
||||
Rails.cache.write(additionals_query_cache_key(object_type),
|
||||
filters: @query.filters,
|
||||
group_by: @query.group_by,
|
||||
column_names: @query.column_names,
|
||||
totalable_names: @query.totalable_names,
|
||||
sort_criteria: params[:sort])
|
||||
elsif session_data.present?
|
||||
@query.sort_criteria = session_data[:sort_criteria]
|
||||
end
|
||||
end
|
||||
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)
|
||||
raise ::Unauthorized unless @query.visible?
|
||||
|
||||
@query.project = @project
|
||||
@query.user_filter = options[:user_filter] if options[:user_filter]
|
||||
session[session_key] = { id: @query.id, project_id: @query.project_id }
|
||||
|
||||
@query.sort_criteria = params[:sort] if params[:sort].present?
|
||||
# we have to write cache for sort order
|
||||
Rails.cache.write(additionals_query_cache_key(object_type),
|
||||
filters: @query.filters,
|
||||
group_by: @query.group_by,
|
||||
column_names: @query.column_names,
|
||||
totalable_names: @query.totalable_names,
|
||||
sort_criteria: @query.sort_criteria)
|
||||
end
|
||||
|
||||
def additionals_query_cache_key(object_type)
|
||||
project_id = @project.nil? ? 0 : @project.id
|
||||
"#{object_type}_query_data_#{session.id}_#{project_id}"
|
||||
end
|
||||
|
||||
def additionals_select2_search_users(where_filter = '', where_params = {})
|
||||
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.order(last_login_on: :desc)
|
||||
.limit(params[: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
|
||||
|
||||
def additionals_query_to_xlsx(items, query, options = {})
|
||||
require 'write_xlsx'
|
||||
columns = if options[:columns].present? || options[:c].present?
|
||||
query.available_columns
|
||||
else
|
||||
query.columns
|
||||
end
|
||||
|
||||
stream = StringIO.new('')
|
||||
export_to_xlsx(items, columns, filename: stream)
|
||||
stream.string
|
||||
end
|
||||
|
||||
def additionals_result_to_xlsx(items, columns, options = {})
|
||||
raise 'option filename is mission' if options[:filename].blank?
|
||||
|
||||
require 'write_xlsx'
|
||||
export_to_xlsx(items, columns, options)
|
||||
end
|
||||
|
||||
def export_to_xlsx(items, columns, options)
|
||||
workbook = WriteXLSX.new(options[:filename])
|
||||
worksheet = workbook.add_worksheet
|
||||
|
||||
# Freeze header row and # column.
|
||||
freeze_row = options[:freeze_first_row].nil? || options[:freeze_first_row] ? 1 : 0
|
||||
freeze_column = options[:freeze_first_column].nil? || options[:freeze_first_column] ? 1 : 0
|
||||
worksheet.freeze_panes(freeze_row, freeze_column)
|
||||
|
||||
options[:columns_width] = if options[:xlsx_write_header_row].present?
|
||||
send(options[:xlsx_write_header_row], workbook, worksheet, columns)
|
||||
else
|
||||
xlsx_write_header_row(workbook, worksheet, columns)
|
||||
end
|
||||
options[:columns_width] = if options[:xlsx_write_item_rows].present?
|
||||
send(options[:xlsx_write_item_rows], workbook, worksheet, columns, items, options)
|
||||
else
|
||||
xlsx_write_item_rows(workbook, worksheet, columns, items, options)
|
||||
end
|
||||
columns.size.times do |index|
|
||||
worksheet.set_column(index, index, options[:columns_width][index])
|
||||
end
|
||||
|
||||
workbook.close
|
||||
end
|
||||
|
||||
def xlsx_write_header_row(workbook, worksheet, columns)
|
||||
columns_width = []
|
||||
columns.each_with_index do |c, index|
|
||||
value = if c.class.name == 'String'
|
||||
c
|
||||
else
|
||||
c.caption.to_s
|
||||
end
|
||||
|
||||
worksheet.write(0, index, value, workbook.add_format(xlsx_cell_format(:header)))
|
||||
columns_width << xlsx_get_column_width(value)
|
||||
end
|
||||
columns_width
|
||||
end
|
||||
|
||||
def xlsx_write_item_rows(workbook, worksheet, columns, items, options = {})
|
||||
hyperlink_format = workbook.add_format(xlsx_cell_format(:link))
|
||||
even_text_format = workbook.add_format(xlsx_cell_format(:text, '', 0))
|
||||
even_text_format.set_num_format(0x31)
|
||||
odd_text_format = workbook.add_format(xlsx_cell_format(:text, '', 1))
|
||||
odd_text_format.set_num_format(0x31)
|
||||
|
||||
items.each_with_index do |line, line_index|
|
||||
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)
|
||||
elsif xlsx_hyperlink_cell?(value)
|
||||
worksheet.write(line_index + 1, column_index, value, hyperlink_format, value)
|
||||
elsif !c.inline?
|
||||
# block column can be multiline strings
|
||||
value.gsub!("\r\n", "\n")
|
||||
text_format = line_index.even? ? even_text_format : odd_text_format
|
||||
worksheet.write_rich_string(line_index + 1, column_index, value, text_format)
|
||||
else
|
||||
worksheet.write(line_index + 1,
|
||||
column_index,
|
||||
value,
|
||||
workbook.add_format(xlsx_cell_format(:cell, value, line_index)))
|
||||
end
|
||||
|
||||
width = xlsx_get_column_width(value)
|
||||
options[:columns_width][column_index] = width if options[:columns_width][column_index] < width
|
||||
end
|
||||
end
|
||||
options[:columns_width]
|
||||
end
|
||||
|
||||
def xlsx_get_column_width(value)
|
||||
value_str = value.to_s
|
||||
|
||||
# 1.1: margin
|
||||
width = (value_str.length + value_str.chars.reject(&:ascii_only?).length) * 1.1 + 1
|
||||
# 30: max width
|
||||
width > 30 ? 30 : width
|
||||
end
|
||||
|
||||
def xlsx_cell_format(type, value = 0, index = 0)
|
||||
format = { border: 1, text_wrap: 1, valign: 'top' }
|
||||
case type
|
||||
when :header
|
||||
format[:bold] = 1
|
||||
format[:color] = 'white'
|
||||
format[:bg_color] = 'gray'
|
||||
when :link
|
||||
format[:color] = 'blue'
|
||||
format[:underline] = 1
|
||||
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
|
||||
end
|
||||
|
||||
format
|
||||
end
|
||||
|
||||
def xlsx_hyperlink_cell?(token)
|
||||
# Match http, https or ftp URL
|
||||
if token =~ %r{\A[fh]tt?ps?://}
|
||||
true
|
||||
# Match mailto:
|
||||
elsif token.present? && token.start_with?('mailto:')
|
||||
true
|
||||
# Match internal or external sheet link
|
||||
elsif token =~ /\A(?:in|ex)ternal:/
|
||||
true
|
||||
end
|
||||
end
|
||||
|
||||
# Returns the query definition as hidden field tags
|
||||
# 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)
|
||||
tags = hidden_field_tag('set_filter', '1', id: nil)
|
||||
|
||||
if query.filters.present?
|
||||
query.filters.each do |field, filter|
|
||||
tags << hidden_field_tag('f[]', field, id: nil)
|
||||
tags << hidden_field_tag("op[#{field}]", filter[:operator], id: nil)
|
||||
filter[:values].each do |value|
|
||||
tags << hidden_field_tag("v[#{field}][]", value, id: nil)
|
||||
end
|
||||
end
|
||||
else
|
||||
tags << hidden_field_tag('f[]', '', id: nil)
|
||||
end
|
||||
query.columns.each do |column|
|
||||
next if ignored_column_names.present? && ignored_column_names.include?(column.name)
|
||||
|
||||
tags << hidden_field_tag('c[]', column.name, id: nil)
|
||||
end
|
||||
if query.totalable_names.present?
|
||||
query.totalable_names.each do |name|
|
||||
tags << hidden_field_tag('t[]', name, id: nil)
|
||||
end
|
||||
end
|
||||
tags << hidden_field_tag('group_by', query.group_by, id: nil) if query.group_by.present?
|
||||
tags << hidden_field_tag('sort', query.sort_criteria.to_param, id: nil) if query.sort_criteria.present?
|
||||
|
||||
tags
|
||||
end
|
||||
end
|
125
plugins/additionals/app/helpers/additionals_tag_helper.rb
Executable file
125
plugins/additionals/app/helpers/additionals_tag_helper.rb
Executable file
|
@ -0,0 +1,125 @@
|
|||
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?
|
||||
|
||||
options[:show_count] = true
|
||||
|
||||
# prevent ActsAsTaggableOn::TagsHelper from calling `all`
|
||||
# otherwise we will need sort tags after `tag_cloud`
|
||||
tags = tags.all if tags.respond_to?(:all)
|
||||
|
||||
s = []
|
||||
tags.each do |tag|
|
||||
s << additionals_tag_link(tag, options)
|
||||
end
|
||||
|
||||
sep = if options[:tags_without_color]
|
||||
', '
|
||||
else
|
||||
' '
|
||||
end
|
||||
|
||||
content_tag(:div, safe_join(s, sep), class: 'tags')
|
||||
end
|
||||
|
||||
# plain list of tags
|
||||
def render_additionals_tags(tags, sep = ' ')
|
||||
s = if tags.blank?
|
||||
['']
|
||||
else
|
||||
tags.map(&:name)
|
||||
end
|
||||
s.join(sep)
|
||||
end
|
||||
|
||||
def additionals_tag_links(tag_list, options = {})
|
||||
return unless tag_list
|
||||
|
||||
sep = if options[:tags_without_color]
|
||||
', '
|
||||
else
|
||||
' '
|
||||
end
|
||||
|
||||
safe_join(tag_list.map do |tag|
|
||||
additionals_tag_link(tag, options)
|
||||
end, sep)
|
||||
end
|
||||
|
||||
def additionals_tag_link(tag, options = {})
|
||||
tag_name = []
|
||||
tag_name << tag.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_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]
|
||||
|
||||
if options[:tags_without_color]
|
||||
content_tag('span',
|
||||
link_to(safe_join(tag_name), additionals_tag_url(tag.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)
|
||||
end
|
||||
end
|
||||
|
||||
def additionals_tag_url(tag_name, options = {})
|
||||
action = options[:tag_action].presence || (controller_name == 'hrm_user_resources' ? 'show' : 'index')
|
||||
|
||||
{ controller: options[:tag_controller].presence || controller_name,
|
||||
action: action,
|
||||
set_filter: 1,
|
||||
project_id: @project,
|
||||
fields: [:tags],
|
||||
values: { tags: [tag_name] },
|
||||
operators: { tags: '=' } }
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def tag_cloud(tags, classes)
|
||||
return [] if tags.empty?
|
||||
|
||||
max_count = tags.max_by(&:count).count.to_f
|
||||
|
||||
tags.each do |tag|
|
||||
index = ((tag.count / max_count) * (classes.size - 1))
|
||||
yield tag, classes[index.nan? ? 0 : index.round]
|
||||
end
|
||||
end
|
||||
|
||||
def additionals_tag_color(tag_name)
|
||||
"##{Digest::MD5.hexdigest(tag_name)[0..5]}"
|
||||
end
|
||||
|
||||
def additionals_tag_fg_color(bg_color)
|
||||
# calculate contrast text color according to YIQ method
|
||||
# https://24ways.org/2010/calculating-color-contrast/
|
||||
# https://stackoverflow.com/questions/3942878/how-to-decide-font-color-in-white-or-black-depending-on-background-color
|
||||
r = bg_color[1..2].hex
|
||||
g = bg_color[3..4].hex
|
||||
b = bg_color[5..6].hex
|
||||
(r * 299 + g * 587 + b * 114) >= 128_000 ? 'black' : 'white'
|
||||
end
|
||||
end
|
35
plugins/additionals/app/helpers/additionals_wiki_pdf_helper.rb
Executable file
35
plugins/additionals/app/helpers/additionals_wiki_pdf_helper.rb
Executable file
|
@ -0,0 +1,35 @@
|
|||
module AdditionalsWikiPdfHelper
|
||||
include Redmine::Export::PDF
|
||||
|
||||
def wiki_page_to_pdf(page, project)
|
||||
pdf = ITCPDF.new(current_language)
|
||||
pdf.set_title("#{project} - #{page.title}")
|
||||
pdf.alias_nb_pages
|
||||
pdf.footer_date = format_date(User.current.today)
|
||||
pdf.add_page
|
||||
unless Additionals.setting?(:wiki_pdf_remove_title)
|
||||
pdf.SetFontStyle('B', 11)
|
||||
pdf.RDMMultiCell(190, 5,
|
||||
"#{project} - #{page.title} - # #{page.content.version}")
|
||||
end
|
||||
pdf.ln
|
||||
# Set resize image scale
|
||||
pdf.set_image_scale(1.6)
|
||||
pdf.SetFontStyle('', 9)
|
||||
if Additionals.setting?(:wiki_pdf_remove_attachments)
|
||||
pdf.RDMwriteFormattedCell(190,
|
||||
5,
|
||||
'',
|
||||
'',
|
||||
textilizable(page.content,
|
||||
:text,
|
||||
only_path: false,
|
||||
edit_section_links: false,
|
||||
headings: false,
|
||||
inline_attachments: false), page.attachments)
|
||||
else
|
||||
write_wiki_page(pdf, page)
|
||||
end
|
||||
pdf.output
|
||||
end
|
||||
end
|
Loading…
Add table
Add a link
Reference in a new issue