Nuevo plugin Additionals 2.0.20
This commit is contained in:
parent
a2a901b71b
commit
93e1e28683
354 changed files with 40514 additions and 0 deletions
39
plugins/additionals/app/controllers/additionals_assign_to_me_controller.rb
Executable file
39
plugins/additionals/app/controllers/additionals_assign_to_me_controller.rb
Executable file
|
@ -0,0 +1,39 @@
|
|||
class AdditionalsAssignToMeController < ApplicationController
|
||||
before_action :find_issue
|
||||
helper :additionals_issues
|
||||
|
||||
def update
|
||||
old_user = @issue.assigned_to
|
||||
user_in_project = @project.assignable_users.detect { |u| u.id == User.current.id }
|
||||
|
||||
if old_user == User.current || user_in_project.nil?
|
||||
redirect_to(issue_path(@issue))
|
||||
return
|
||||
end
|
||||
|
||||
@issue.init_journal(User.current)
|
||||
@issue.assigned_to = User.current
|
||||
|
||||
if !@issue.save || old_user == @issue.assigned_to
|
||||
flash[:error] = l(:error_issues_could_not_be_assigned_to_me)
|
||||
return redirect_to(issue_path(@issue))
|
||||
end
|
||||
|
||||
last_journal = @issue.journals.visible.order(:created_on).last
|
||||
return redirect_to(issue_path(@issue)) if last_journal.nil?
|
||||
|
||||
last_journal = @issue.journals.visible.order(:created_on).last
|
||||
redirect_to "#{issue_path(@issue)}#change-#{last_journal.id}"
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def find_issue
|
||||
@issue = Issue.find(params[:issue_id])
|
||||
raise Unauthorized unless @issue.visible? && @issue.editable?
|
||||
|
||||
@project = @issue.project
|
||||
rescue ActiveRecord::RecordNotFound
|
||||
render_404
|
||||
end
|
||||
end
|
43
plugins/additionals/app/controllers/additionals_change_status_controller.rb
Executable file
43
plugins/additionals/app/controllers/additionals_change_status_controller.rb
Executable file
|
@ -0,0 +1,43 @@
|
|||
class AdditionalsChangeStatusController < ApplicationController
|
||||
before_action :find_issue
|
||||
helper :additionals_issues
|
||||
|
||||
def update
|
||||
issue_old_status_id = @issue.status.id
|
||||
issue_old_user = @issue.assigned_to
|
||||
new_status_id = params[:new_status_id].to_i
|
||||
allowed_status = @issue.sidbar_change_status_allowed_to(User.current, new_status_id)
|
||||
|
||||
if new_status_id < 1 || @issue.status_id == new_status_id || allowed_status.nil?
|
||||
redirect_to(issue_path(@issue))
|
||||
return
|
||||
end
|
||||
|
||||
@issue.init_journal(User.current)
|
||||
@issue.status_id = new_status_id
|
||||
@issue.assigned_to = User.current if @issue.status_x_affected?(new_status_id) && issue_old_user != User.current
|
||||
|
||||
if !@issue.save || issue_old_status_id == @issue.status_id
|
||||
flash[:error] = l(:error_issue_status_could_not_changed)
|
||||
return redirect_to(issue_path(@issue))
|
||||
end
|
||||
|
||||
last_journal = @issue.journals.visible.order(:created_on).last
|
||||
|
||||
return redirect_to(issue_path(@issue)) if last_journal.nil?
|
||||
|
||||
last_journal = @issue.journals.visible.order(:created_on).last
|
||||
redirect_to "#{issue_path(@issue)}#change-#{last_journal.id}"
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def find_issue
|
||||
@issue = Issue.find(params[:issue_id])
|
||||
raise Unauthorized unless @issue.visible? && @issue.editable?
|
||||
|
||||
@project = @issue.project
|
||||
rescue ActiveRecord::RecordNotFound
|
||||
render_404
|
||||
end
|
||||
end
|
7
plugins/additionals/app/controllers/additionals_macros_controller.rb
Executable file
7
plugins/additionals/app/controllers/additionals_macros_controller.rb
Executable file
|
@ -0,0 +1,7 @@
|
|||
class AdditionalsMacrosController < ApplicationController
|
||||
before_action :require_login
|
||||
|
||||
def show
|
||||
@available_macros = AdditionalsMacro.all
|
||||
end
|
||||
end
|
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
|
118
plugins/additionals/app/models/additionals_font_awesome.rb
Executable file
118
plugins/additionals/app/models/additionals_font_awesome.rb
Executable file
|
@ -0,0 +1,118 @@
|
|||
class AdditionalsFontAwesome
|
||||
include Redmine::I18n
|
||||
|
||||
class << self
|
||||
def load_icons(type)
|
||||
data = YAML.safe_load(ERB.new(IO.read(Rails.root.join('plugins',
|
||||
'additionals',
|
||||
'config',
|
||||
'fontawesome_icons.yml'))).result) || {}
|
||||
icons = {}
|
||||
data.each do |key, values|
|
||||
icons[key] = { unicode: values['unicode'], label: values['label'] } if values['styles'].include?(convert_type2style(type))
|
||||
end
|
||||
icons
|
||||
end
|
||||
|
||||
def convert_type2style(type)
|
||||
case type
|
||||
when :fab
|
||||
'brands'
|
||||
when :far
|
||||
'regular'
|
||||
else
|
||||
'solid'
|
||||
end
|
||||
end
|
||||
|
||||
def font_weight(key)
|
||||
case key
|
||||
when :fas
|
||||
900
|
||||
else
|
||||
'normal'
|
||||
end
|
||||
end
|
||||
|
||||
def font_family(key)
|
||||
case key
|
||||
when :fab
|
||||
'Font Awesome\ 5 Brands'
|
||||
else
|
||||
'Font Awesome\ 5 Free'
|
||||
end
|
||||
end
|
||||
|
||||
def key2value(key, type)
|
||||
"fa#{type}_" + key
|
||||
end
|
||||
|
||||
def classes(value)
|
||||
info = value_info(value)
|
||||
return '' if info.blank?
|
||||
|
||||
info[:classes]
|
||||
end
|
||||
|
||||
def json_values(type)
|
||||
FONTAWESOME_ICONS[type].collect { |fa_symbol, values| { id: key2value(fa_symbol, type[-1]), text: values[:label] } }
|
||||
end
|
||||
|
||||
def select_values(type)
|
||||
FONTAWESOME_ICONS[type].collect { |fa_symbol, values| [values[:label], key2value(fa_symbol, type[-1])] }
|
||||
end
|
||||
|
||||
def json_for_select
|
||||
[{ text: l(:label_fontawesome_regular), children: json_values(:far) },
|
||||
{ text: l(:label_fontawesome_solid), children: json_values(:fas) },
|
||||
{ text: l(:label_fontawesome_brands), children: json_values(:fab) }].to_json
|
||||
end
|
||||
|
||||
# show only one value as current selected
|
||||
# (all other options are retrieved by select2
|
||||
def active_option_for_select(selected)
|
||||
info = value_info(selected, with_details: true)
|
||||
return [] if info.blank?
|
||||
|
||||
[[info[:label], selected]]
|
||||
end
|
||||
|
||||
def options_for_select
|
||||
[[l(:label_fontawesome_regular), select_values(:far)],
|
||||
[l(:label_fontawesome_solid), select_values(:fas)],
|
||||
[l(:label_fontawesome_brands), select_values(:fab)]]
|
||||
end
|
||||
|
||||
def value_info(value, options = {})
|
||||
return {} if value.blank?
|
||||
|
||||
values = value.split('_')
|
||||
return {} unless values.count == 2
|
||||
|
||||
info = { type: values[0].to_sym,
|
||||
name: "fa-#{values[1]}" }
|
||||
|
||||
info[:classes] = "#{info[:type]} #{info[:name]}"
|
||||
info[:font_weight] = font_weight(info[:type])
|
||||
info[:font_family] = font_family(info[:type])
|
||||
|
||||
if options[:with_details]
|
||||
info.merge!(load_details(info[:type], values[1]))
|
||||
return {} if info[:unicode].blank?
|
||||
end
|
||||
|
||||
info
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def load_details(type, name)
|
||||
return {} unless FONTAWESOME_ICONS.key?(type)
|
||||
|
||||
values = FONTAWESOME_ICONS[type][name]
|
||||
return {} if values.blank?
|
||||
|
||||
{ unicode: "&#x#{values[:unicode]};".html_safe, label: values[:label] } # rubocop:disable Rails/OutputSafety
|
||||
end
|
||||
end
|
||||
end
|
43
plugins/additionals/app/models/additionals_import.rb
Executable file
43
plugins/additionals/app/models/additionals_import.rb
Executable file
|
@ -0,0 +1,43 @@
|
|||
class AdditionalsImport < Import
|
||||
class_attribute :import_class
|
||||
|
||||
# Returns the objects that were imported
|
||||
def saved_objects
|
||||
object_ids = saved_items.pluck(:obj_id)
|
||||
import_class.where(id: object_ids).order(:id)
|
||||
end
|
||||
|
||||
def project=(project)
|
||||
settings['project'] = project
|
||||
end
|
||||
|
||||
def project
|
||||
settings['project']
|
||||
end
|
||||
|
||||
def mappable_custom_fields
|
||||
object = import_class.new
|
||||
@custom_fields = object.custom_field_values.map(&:custom_field)
|
||||
end
|
||||
|
||||
def build_custom_field_attributes(object, row)
|
||||
object.custom_field_values.each_with_object({}) do |v, h|
|
||||
value = case v.custom_field.field_format
|
||||
when 'date'
|
||||
row_date(row, "cf_#{v.custom_field.id}")
|
||||
when 'list'
|
||||
row_value(row, "cf_#{v.custom_field.id}").try(:split, ',')
|
||||
else
|
||||
row_value(row, "cf_#{v.custom_field.id}")
|
||||
end
|
||||
next unless value
|
||||
|
||||
h[v.custom_field.id.to_s] =
|
||||
if value.is_a?(Array)
|
||||
value.map { |val| v.custom_field.value_from_keyword(val.strip, object) }.compact.flatten
|
||||
else
|
||||
v.custom_field.value_from_keyword(value, object)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
75
plugins/additionals/app/models/additionals_macro.rb
Executable file
75
plugins/additionals/app/models/additionals_macro.rb
Executable file
|
@ -0,0 +1,75 @@
|
|||
class AdditionalsMacro
|
||||
def self.all(options = {})
|
||||
all = Redmine::WikiFormatting::Macros.available_macros
|
||||
macros = {}
|
||||
macro_list = []
|
||||
|
||||
# needs to run every request (for each user once)
|
||||
permissions = build_permissions(options)
|
||||
|
||||
if options[:filtered].present?
|
||||
options[:filtered] << 'hello_world'
|
||||
else
|
||||
options[:filtered] = ['hello_world']
|
||||
end
|
||||
|
||||
all.each do |macro, macro_options|
|
||||
next if options[:filtered].include?(macro.to_s)
|
||||
next unless macro_allowed(macro, permissions)
|
||||
|
||||
macro_list << macro.to_s
|
||||
macros[macro] = macro_options
|
||||
end
|
||||
|
||||
if options[:only_names]
|
||||
macro_list.sort
|
||||
else
|
||||
macros.sort
|
||||
end
|
||||
end
|
||||
|
||||
def self.macro_allowed(macro, permissions)
|
||||
permissions.each do |permission|
|
||||
next if permission[:list].exclude?(macro)
|
||||
return false unless permission[:access]
|
||||
end
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
def self.build_permissions(options)
|
||||
gpermission = []
|
||||
macro_permissions.each do |permission|
|
||||
permission[:access] = if options[:controller_only] &&
|
||||
permission[:controller].present? &&
|
||||
options[:controller_only].to_sym != permission[:controller]
|
||||
false
|
||||
else
|
||||
User.current.allowed_to?(permission[:permission], nil, global: true)
|
||||
end
|
||||
gpermission << permission
|
||||
end
|
||||
|
||||
gpermission
|
||||
end
|
||||
|
||||
def self.macro_permissions
|
||||
[{ list: %i[issue issue_name_link],
|
||||
permission: :view_issues },
|
||||
{ list: %i[password password_query password_tag password_tag_count],
|
||||
permission: :view_passwords },
|
||||
{ list: %i[contact deal contact_avatar contact_note contact_plain],
|
||||
permission: :view_contacts },
|
||||
{ list: %i[db db_query db_tag db_tag_count],
|
||||
permission: :view_db_entries },
|
||||
{ list: %i[child_pages calendar last_updated_at last_updated_by lastupdated_at lastupdated_by
|
||||
new_page recently_updated recent comments comment_form tags taggedpages tagcloud
|
||||
show_count count vote show_vote terms_accept terms_reject],
|
||||
permission: :view_wiki_pages,
|
||||
controller: :wiki },
|
||||
{ list: %i[mail send_file],
|
||||
permission: :view_helpdesk_tickets },
|
||||
{ list: %i[kb article_id article category],
|
||||
permission: :view_kb_articles }]
|
||||
end
|
||||
end
|
150
plugins/additionals/app/models/additionals_query.rb
Executable file
150
plugins/additionals/app/models/additionals_query.rb
Executable file
|
@ -0,0 +1,150 @@
|
|||
module AdditionalsQuery
|
||||
def self.included(base)
|
||||
base.send :include, InstanceMethods
|
||||
end
|
||||
|
||||
module InstanceMethods
|
||||
def initialize_ids_filter(options = {})
|
||||
if options[:label]
|
||||
add_available_filter 'ids', type: :integer, label: options[:label]
|
||||
else
|
||||
add_available_filter 'ids', type: :integer, name: '#'
|
||||
end
|
||||
end
|
||||
|
||||
def sql_for_ids_field(_field, operator, value)
|
||||
if operator == '='
|
||||
# accepts a comma separated list of ids
|
||||
ids = value.first.to_s.scan(/\d+/).map(&:to_i)
|
||||
if ids.present?
|
||||
"#{queried_table_name}.id IN (#{ids.join(',')})"
|
||||
else
|
||||
'1=0'
|
||||
end
|
||||
else
|
||||
sql_for_field('id', operator, value, queried_table_name, 'id')
|
||||
end
|
||||
end
|
||||
|
||||
def initialize_project_filter(options = {})
|
||||
if project.nil?
|
||||
add_available_filter('project_id', order: options[:position],
|
||||
type: :list,
|
||||
values: -> { project_values })
|
||||
end
|
||||
return if project.nil? || project.leaf? || subproject_values.empty?
|
||||
|
||||
add_available_filter('subproject_id', order: options[:position],
|
||||
type: :list_subprojects,
|
||||
values: -> { subproject_values })
|
||||
end
|
||||
|
||||
def initialize_created_filter(options = {})
|
||||
add_available_filter 'created_on', order: options[:position],
|
||||
type: :date_past,
|
||||
label: options[:label].presence
|
||||
end
|
||||
|
||||
def initialize_updated_filter(options = {})
|
||||
add_available_filter 'updated_on', order: options[:position],
|
||||
type: :date_past,
|
||||
label: options[:label].presence
|
||||
end
|
||||
|
||||
def initialize_tags_filter(options = {})
|
||||
values = if project
|
||||
queried_class.available_tags(project: project.id)
|
||||
else
|
||||
queried_class.available_tags
|
||||
end
|
||||
return if values.blank?
|
||||
|
||||
add_available_filter 'tags', order: options[:position],
|
||||
type: :list,
|
||||
values: values.collect { |t| [t.name, t.name] }
|
||||
end
|
||||
|
||||
def initialize_author_filter(options = {})
|
||||
return if author_values.empty?
|
||||
|
||||
add_available_filter('author_id', order: options[:position],
|
||||
type: :list_optional,
|
||||
values: options[:no_lambda].nil? ? author_values : -> { author_values })
|
||||
end
|
||||
|
||||
def initialize_assignee_filter(options = {})
|
||||
return if author_values.empty?
|
||||
|
||||
add_available_filter('assigned_to_id', order: options[:position],
|
||||
type: :list_optional,
|
||||
values: options[:no_lambda] ? author_values : -> { author_values })
|
||||
end
|
||||
|
||||
def initialize_watcher_filter(options = {})
|
||||
return if watcher_values.empty? || !User.current.logged?
|
||||
|
||||
add_available_filter('watcher_id', order: options[:position],
|
||||
type: :list,
|
||||
values: options[:no_lambda] ? watcher_values : -> { watcher_values })
|
||||
end
|
||||
|
||||
def watcher_values
|
||||
watcher_values = [["<< #{l(:label_me)} >>", 'me']]
|
||||
watcher_values += users.collect { |s| [s.name, s.id.to_s] } if User.current.allowed_to?(:manage_public_queries, project, global: true)
|
||||
watcher_values
|
||||
end
|
||||
|
||||
def sql_for_watcher_id_field(field, operator, value)
|
||||
watchable_type = queried_class == User ? 'Principal' : queried_class.to_s
|
||||
|
||||
db_table = Watcher.table_name
|
||||
"#{queried_table_name}.id #{operator == '=' ? 'IN' : 'NOT IN'}
|
||||
(SELECT #{db_table}.watchable_id FROM #{db_table} WHERE #{db_table}.watchable_type='#{watchable_type}' AND " +
|
||||
sql_for_field(field, '=', value, db_table, 'user_id') + ')'
|
||||
end
|
||||
|
||||
def sql_for_tags_field(field, _operator, value)
|
||||
AdditionalsTag.sql_for_tags_field(queried_class, operator_for(field), value)
|
||||
end
|
||||
|
||||
def sql_for_is_private_field(_field, operator, value)
|
||||
if bool_operator(operator, value)
|
||||
return '1=1' if value.count > 1
|
||||
|
||||
"#{queried_table_name}.is_private = #{self.class.connection.quoted_true}"
|
||||
else
|
||||
return '1=0' if value.count > 1
|
||||
|
||||
"#{queried_table_name}.is_private = #{self.class.connection.quoted_false}"
|
||||
end
|
||||
end
|
||||
|
||||
# use for list fields with to values 1 (true) and 0 (false)
|
||||
def bool_operator(operator, values)
|
||||
operator == '=' && values.first == '1' || operator != '=' && values.first != '1'
|
||||
end
|
||||
|
||||
# use for list
|
||||
def bool_values
|
||||
[[l(:general_text_yes), '1'], [l(:general_text_no), '0']]
|
||||
end
|
||||
|
||||
def query_count
|
||||
objects_scope.count
|
||||
rescue ::ActiveRecord::StatementInvalid => e
|
||||
raise StatementInvalid, e.message
|
||||
end
|
||||
|
||||
def results_scope(options = {})
|
||||
order_option = [group_by_sort_order, (options[:order] || sort_clause)].flatten.reject(&:blank?)
|
||||
|
||||
objects_scope(options)
|
||||
.order(order_option)
|
||||
.joins(joins_for_order_statement(order_option.join(',')))
|
||||
.limit(options[:limit])
|
||||
.offset(options[:offset])
|
||||
rescue ::ActiveRecord::StatementInvalid => e
|
||||
raise StatementInvalid, e.message
|
||||
end
|
||||
end
|
||||
end
|
70
plugins/additionals/app/models/additionals_tag.rb
Executable file
70
plugins/additionals/app/models/additionals_tag.rb
Executable file
|
@ -0,0 +1,70 @@
|
|||
class AdditionalsTag
|
||||
TAG_TABLE_NAME = RedmineCrm::Tag.table_name if defined? RedmineCrm
|
||||
TAGGING_TABLE_NAME = RedmineCrm::Tagging.table_name if defined? RedmineCrm
|
||||
PROJECT_TABLE_NAME = Project.table_name
|
||||
|
||||
def self.get_available_tags(klass, options = {})
|
||||
scope = RedmineCrm::Tag.where({})
|
||||
scope = scope.where("#{PROJECT_TABLE_NAME}.id = ?", options[:project]) if options[:project]
|
||||
if options[:permission]
|
||||
scope = scope.where(tag_access(options[:permission]))
|
||||
elsif options[:visible_condition]
|
||||
scope = scope.where(klass.visible_condition(User.current))
|
||||
end
|
||||
scope = scope.where("LOWER(#{TAG_TABLE_NAME}.name) LIKE ?", "%#{options[:name_like].downcase}%") if options[:name_like]
|
||||
scope = scope.where("#{TAG_TABLE_NAME}.name=?", options[:name]) if options[:name]
|
||||
scope = scope.where("#{TAGGING_TABLE_NAME}.taggable_id!=?", options[:exclude_id]) if options[:exclude_id]
|
||||
scope = scope.where(options[:where_field] => options[:where_value]) if options[:where_field].present? && options[:where_value]
|
||||
|
||||
scope = scope.select("#{TAG_TABLE_NAME}.*, COUNT(DISTINCT #{TAGGING_TABLE_NAME}.taggable_id) AS count")
|
||||
scope = scope.joins(tag_joins(klass, options))
|
||||
scope = scope.group("#{TAG_TABLE_NAME}.id, #{TAG_TABLE_NAME}.name").having('COUNT(*) > 0')
|
||||
scope = scope.order("#{TAG_TABLE_NAME}.name")
|
||||
scope
|
||||
end
|
||||
|
||||
def self.tag_joins(klass, options = {})
|
||||
table_name = klass.table_name
|
||||
|
||||
joins = ["JOIN #{TAGGING_TABLE_NAME} ON #{TAGGING_TABLE_NAME}.tag_id = #{TAG_TABLE_NAME}.id"]
|
||||
joins << "JOIN #{table_name} " \
|
||||
"ON #{table_name}.id = #{TAGGING_TABLE_NAME}.taggable_id AND #{TAGGING_TABLE_NAME}.taggable_type = '#{klass}'"
|
||||
|
||||
if options[:project_join]
|
||||
joins << options[:project_join]
|
||||
elsif options[:project] || !options[:without_projects]
|
||||
joins << "JOIN #{PROJECT_TABLE_NAME} ON #{table_name}.project_id = #{PROJECT_TABLE_NAME}.id"
|
||||
end
|
||||
|
||||
joins
|
||||
end
|
||||
|
||||
def self.tag_access(permission)
|
||||
projects_allowed = if permission.nil?
|
||||
Project.visible.pluck(:id)
|
||||
else
|
||||
Project.where(Project.allowed_to_condition(User.current, permission)).pluck(:id)
|
||||
end
|
||||
|
||||
if projects_allowed.present?
|
||||
"#{PROJECT_TABLE_NAME}.id IN (#{projects_allowed.join(',')})" unless projects_allowed.empty?
|
||||
else
|
||||
'1=0'
|
||||
end
|
||||
end
|
||||
|
||||
def self.remove_unused_tags
|
||||
unused = RedmineCrm::Tag.find_by_sql(<<-SQL)
|
||||
SELECT * FROM tags WHERE id NOT IN (
|
||||
SELECT DISTINCT tag_id FROM taggings
|
||||
)
|
||||
SQL
|
||||
unused.each(&:destroy)
|
||||
end
|
||||
|
||||
def self.sql_for_tags_field(klass, operator, value)
|
||||
compare = operator.eql?('=') ? 'IN' : 'NOT IN'
|
||||
ids_list = klass.tagged_with(value).collect(&:id).push(0).join(',')
|
||||
"( #{klass.table_name}.id #{compare} (#{ids_list}) ) "
|
||||
end
|
||||
end
|
5
plugins/additionals/app/overrides/account/register.rb
Executable file
5
plugins/additionals/app/overrides/account/register.rb
Executable file
|
@ -0,0 +1,5 @@
|
|||
Deface::Override.new virtual_path: 'account/register',
|
||||
name: 'add-invisble-captcha',
|
||||
insert_top: 'div.box',
|
||||
original: 'e64d82c46cc3322e4d953aa119d1e71e81854158',
|
||||
partial: 'account/invisible_captcha'
|
5
plugins/additionals/app/overrides/admin/info.rb
Executable file
5
plugins/additionals/app/overrides/admin/info.rb
Executable file
|
@ -0,0 +1,5 @@
|
|||
Deface::Override.new virtual_path: 'admin/info',
|
||||
name: 'add-system_info',
|
||||
insert_after: 'table.list',
|
||||
original: '73b55ca692bcf4db9ecb7a16ec6d6f9e46f08a90',
|
||||
partial: 'admin/system_info'
|
5
plugins/additionals/app/overrides/issues/show.rb
Executable file
5
plugins/additionals/app/overrides/issues/show.rb
Executable file
|
@ -0,0 +1,5 @@
|
|||
Deface::Override.new virtual_path: 'issues/_action_menu',
|
||||
name: 'add-issue-assign-to-me',
|
||||
insert_bottom: 'div.contextual',
|
||||
original: 'c0a30490bb9ac5c5644e674319f17e40c57034d8',
|
||||
partial: 'issues/additionals_action_menu'
|
5
plugins/additionals/app/overrides/layouts/base.rb
Executable file
5
plugins/additionals/app/overrides/layouts/base.rb
Executable file
|
@ -0,0 +1,5 @@
|
|||
Deface::Override.new virtual_path: 'layouts/base',
|
||||
name: 'add-body-header',
|
||||
insert_before: 'div#wrapper',
|
||||
original: '4af81ed701989727953cea2e376c9d83665d7eb2',
|
||||
partial: 'additionals/global_body_header'
|
5
plugins/additionals/app/overrides/roles/form.rb
Executable file
5
plugins/additionals/app/overrides/roles/form.rb
Executable file
|
@ -0,0 +1,5 @@
|
|||
Deface::Override.new virtual_path: 'roles/_form',
|
||||
name: 'roles-form-hide',
|
||||
insert_before: 'p.manage_members_shown',
|
||||
original: 'b2a317f49e0b65ae506c8871f0c2bcc3e8098766',
|
||||
partial: 'roles/additionals_form'
|
10
plugins/additionals/app/overrides/users/show.rb
Executable file
10
plugins/additionals/app/overrides/users/show.rb
Executable file
|
@ -0,0 +1,10 @@
|
|||
Deface::Override.new virtual_path: 'users/show',
|
||||
name: 'user-show-info-hook',
|
||||
insert_top: 'div.splitcontentleft ul:first-child',
|
||||
original: 'aff8d775275e3f33cc45d72b8e2896144be4beff',
|
||||
partial: 'hooks/view_users_show'
|
||||
Deface::Override.new virtual_path: 'users/show',
|
||||
name: 'user-contextual-hook',
|
||||
insert_bottom: 'div.contextual',
|
||||
original: '9d6a7ad6ba0addc68c6b4f6c3b868511bc8eb542',
|
||||
partial: 'hooks/view_users_contextual'
|
15
plugins/additionals/app/overrides/welcome/index.rb
Executable file
15
plugins/additionals/app/overrides/welcome/index.rb
Executable file
|
@ -0,0 +1,15 @@
|
|||
Deface::Override.new virtual_path: 'welcome/index',
|
||||
name: 'add-welcome-bottom-content',
|
||||
insert_after: 'div.splitcontentright',
|
||||
original: 'dd470844bcaa4d7c9dc66e70e6c0c843d42969bf',
|
||||
partial: 'welcome/overview_bottom'
|
||||
Deface::Override.new virtual_path: 'welcome/index',
|
||||
name: 'add-welcome-top-content',
|
||||
insert_before: 'div.splitcontentleft',
|
||||
original: 'e7de0a2e88c5ccb4d1feb7abac239e4b669babed',
|
||||
partial: 'welcome/overview_top'
|
||||
Deface::Override.new virtual_path: 'welcome/index',
|
||||
name: 'remove-welcome-news',
|
||||
replace: 'div.news',
|
||||
original: '163f5df8f0cb2d5009d7f57ad38174ed29201a1a',
|
||||
partial: 'welcome/overview_news'
|
5
plugins/additionals/app/overrides/wiki/show.rb
Executable file
5
plugins/additionals/app/overrides/wiki/show.rb
Executable file
|
@ -0,0 +1,5 @@
|
|||
Deface::Override.new virtual_path: 'wiki/show',
|
||||
name: 'addto-wiki-show',
|
||||
insert_before: 'div.contextual',
|
||||
original: '6b0cb1646d5e2cb23feee1805949e266036581e6',
|
||||
partial: 'wiki/show_additionals'
|
5
plugins/additionals/app/overrides/wiki/sidebar.rb
Executable file
5
plugins/additionals/app/overrides/wiki/sidebar.rb
Executable file
|
@ -0,0 +1,5 @@
|
|||
Deface::Override.new virtual_path: 'wiki/_sidebar',
|
||||
name: 'addto-wiki-sidebar',
|
||||
insert_after: 'ul',
|
||||
original: '07a5375c015a7d96826c9977c4d8889c4a98bb49',
|
||||
partial: 'wiki/global_sidebar'
|
2
plugins/additionals/app/views/account/_invisible_captcha.html.slim
Executable file
2
plugins/additionals/app/views/account/_invisible_captcha.html.slim
Executable file
|
@ -0,0 +1,2 @@
|
|||
- if Additionals.setting?(:invisible_captcha)
|
||||
= invisible_captcha
|
5
plugins/additionals/app/views/account/_login_text.html.slim
Executable file
5
plugins/additionals/app/views/account/_login_text.html.slim
Executable file
|
@ -0,0 +1,5 @@
|
|||
- login_text = Additionals.settings[:account_login_bottom]
|
||||
- if login_text.present?
|
||||
br
|
||||
.login-additionals
|
||||
= textilizable(login_text)
|
9
plugins/additionals/app/views/additionals/_body_bottom.html.slim
Executable file
9
plugins/additionals/app/views/additionals/_body_bottom.html.slim
Executable file
|
@ -0,0 +1,9 @@
|
|||
- footer = Additionals.settings[:global_footer]
|
||||
- if footer.present?
|
||||
.additionals-footer
|
||||
= textilizable(footer)
|
||||
- if @additionals_help_items.present?
|
||||
javascript:
|
||||
$(function() {
|
||||
$('a.help').parent().append("<ul class=\"menu-children\">#{escape_javascript(@additionals_help_items)}</ul>");
|
||||
});
|
5
plugins/additionals/app/views/additionals/_content.html.slim
Executable file
5
plugins/additionals/app/views/additionals/_content.html.slim
Executable file
|
@ -0,0 +1,5 @@
|
|||
- unless (controller_name == 'account' && action_name == 'login') || \
|
||||
(controller_name == 'my') || \
|
||||
(controller_name == 'account' && action_name == 'lost_password')
|
||||
- if Additionals.setting?(:add_go_to_top)
|
||||
a.gototop[href="#gototop"] = l(:label_go_to_top)
|
27
plugins/additionals/app/views/additionals/_export_options.html.slim
Executable file
27
plugins/additionals/app/views/additionals/_export_options.html.slim
Executable file
|
@ -0,0 +1,27 @@
|
|||
div id="#{export_format}-export-options" style="display: none"
|
||||
h3.title = l(:label_export_options, export_format: export_format.upcase)
|
||||
|
||||
= form_tag(url, method: :get, id: "#{export_format}-export-form") do
|
||||
- if @query.available_filters.key?('description')
|
||||
= query_as_hidden_field_tags @query, [:description]
|
||||
else
|
||||
= query_as_hidden_field_tags @query
|
||||
p
|
||||
label
|
||||
= radio_button_tag 'c[]', '', true
|
||||
= l(:description_selected_columns)
|
||||
br
|
||||
label
|
||||
= radio_button_tag 'c[]', 'all_inline'
|
||||
= l(:description_all_columns)
|
||||
- if @query.available_filters.key?('description')
|
||||
p
|
||||
label
|
||||
= check_box_tag 'c[]', 'description', @query.has_column?(:description)
|
||||
= l(:field_description)
|
||||
- if Rails.version >= '5.2'
|
||||
= export_csv_encoding_select_tag
|
||||
p.buttons
|
||||
= submit_tag l(:button_export), name: nil, onclick: 'hideModal(this);'
|
||||
'
|
||||
= link_to_function l(:button_cancel), 'hideModal(this);'
|
2
plugins/additionals/app/views/additionals/_global_body_header.slim
Executable file
2
plugins/additionals/app/views/additionals/_global_body_header.slim
Executable file
|
@ -0,0 +1,2 @@
|
|||
- if Additionals.setting?(:add_go_to_top)
|
||||
a#gototop
|
5
plugins/additionals/app/views/additionals/_global_sidebar.html.slim
Executable file
5
plugins/additionals/app/views/additionals/_global_sidebar.html.slim
Executable file
|
@ -0,0 +1,5 @@
|
|||
- sidebar = Additionals.settings[:global_sidebar]
|
||||
- if sidebar.present?
|
||||
br
|
||||
.sidebar-additionals
|
||||
= textilizable(sidebar)
|
|
@ -0,0 +1,7 @@
|
|||
= render(partial: 'additionals/live_search_ajax_call.js', layout: false, formats: [:js])
|
||||
- unless defined? classes
|
||||
- classes = 'title'
|
||||
h2 class="#{classes}"
|
||||
= @query.new_record? ? l(title) : h(@query.name)
|
||||
span.additionals-live-search
|
||||
= text_field_tag(:search, q, autocomplete: 'off', class: 'live-search-field', placeholder: l(placeholder))
|
9
plugins/additionals/app/views/additionals/_html_head.html.slim
Executable file
9
plugins/additionals/app/views/additionals/_html_head.html.slim
Executable file
|
@ -0,0 +1,9 @@
|
|||
- additionals_top_menu_setup
|
||||
- if Additionals.settings[:external_urls].to_i > 0
|
||||
= javascript_include_tag('redirect', plugin: 'additionals')
|
||||
- if Additionals.settings[:external_urls].to_i == 2
|
||||
= javascript_include_tag('noreferrer', plugin: 'additionals')
|
||||
= additionals_library_load(:font_awesome)
|
||||
= stylesheet_link_tag 'additionals', plugin: 'additionals'
|
||||
- if User.current.try(:hrm_user_type_id).nil?
|
||||
- render_custom_top_menu_item
|
18
plugins/additionals/app/views/additionals/_live_search_ajax_call.js.slim
Executable file
18
plugins/additionals/app/views/additionals/_live_search_ajax_call.js.slim
Executable file
|
@ -0,0 +1,18 @@
|
|||
javascript:
|
||||
$(function() {
|
||||
// when the #search field changes
|
||||
$('#search').live_observe_field(2, function() {
|
||||
var form = $('#query_form'); // grab the form wrapping the search bar.
|
||||
var url = form.attr('action');
|
||||
form.find('[name="c[]"] option').each(function(i, elem) {
|
||||
$(elem).attr('selected', true)
|
||||
})
|
||||
var formData = form.serialize();
|
||||
form.find('[name="c[]"] option').each(function(i, elem) {
|
||||
$(elem).attr('selected', false)
|
||||
})
|
||||
$.get(url, formData, function(data) { // perform an AJAX get, the trailing function is what happens on successful get.
|
||||
$("#query-result-list").html(data); // replace the "results" div with the result of action taken
|
||||
});
|
||||
});
|
||||
});
|
31
plugins/additionals/app/views/additionals/_select2_ajax_call.js.slim
Executable file
31
plugins/additionals/app/views/additionals/_select2_ajax_call.js.slim
Executable file
|
@ -0,0 +1,31 @@
|
|||
- options = {} if options.nil?
|
||||
javascript:
|
||||
$("##{field_id}").select2({
|
||||
ajax: {
|
||||
url: "#{ajax_url}",
|
||||
dataType: 'json',
|
||||
delay: 250,
|
||||
data: function(params) {
|
||||
return {
|
||||
q: params.term
|
||||
};
|
||||
},
|
||||
processResults: function(data, params) {
|
||||
return {
|
||||
results: data
|
||||
};
|
||||
},
|
||||
cache: true
|
||||
},
|
||||
placeholder: "#{options[:placeholder].presence}",
|
||||
allowClear: #{options[:allow_clear].present? && options[:allow_clear] ? 'true' : 'false'},
|
||||
minimumInputLength: 0,
|
||||
width: '60%',
|
||||
templateResult: formatState
|
||||
});
|
||||
|
||||
function formatState(opt) {
|
||||
if (opt.loading) return opt.name;
|
||||
var $opt = $('<span>' + opt.name_with_icon + '</span>');
|
||||
return $opt;
|
||||
};
|
27
plugins/additionals/app/views/additionals/_settings_list_defaults.html.slim
Executable file
27
plugins/additionals/app/views/additionals/_settings_list_defaults.html.slim
Executable file
|
@ -0,0 +1,27 @@
|
|||
fieldset.box
|
||||
legend = l(:additionals_query_list_defaults)
|
||||
- setting_name_columns = "#{query_type}_list_defaults"
|
||||
- query = query_class.new(@settings[setting_name_columns.to_sym])
|
||||
- if Redmine::VERSION.to_s >= '4'
|
||||
.default-query-settings-label-redmine4
|
||||
= render_query_columns_selection(query, name: "settings[#{setting_name_columns}][column_names]")
|
||||
- else
|
||||
.default-query-settings-label
|
||||
= render_query_columns_selection(query, name: "settings[#{setting_name_columns}][column_names]")
|
||||
|
||||
- columns = query_class.new.available_totalable_columns
|
||||
- if columns.count > 0
|
||||
fieldset.box
|
||||
legend = l(:additionals_query_list_default_totals)
|
||||
|
||||
.default-query-settings-totals
|
||||
- setting_name_totals = "#{query_type}_list_default_totals"
|
||||
= hidden_field_tag("settings[#{setting_name_totals}][]", '')
|
||||
- columns.each do |s|
|
||||
label.inline
|
||||
- value = @settings[setting_name_totals.to_sym].present? ? @settings[setting_name_totals.to_sym].include?(s.name.to_s) : false
|
||||
= check_box_tag("settings[#{setting_name_totals}][]",
|
||||
s.name,
|
||||
value,
|
||||
id: nil)
|
||||
= s.caption
|
26
plugins/additionals/app/views/additionals/_tag_list.html.slim
Executable file
26
plugins/additionals/app/views/additionals/_tag_list.html.slim
Executable file
|
@ -0,0 +1,26 @@
|
|||
- if defined?(show_always) && show_always || entry.tag_list.present?
|
||||
.tags.attribute
|
||||
- unless defined? hide_label
|
||||
span.label
|
||||
= l(:field_tag_list)
|
||||
' :
|
||||
- if defined?(editable) && editable
|
||||
#tags-data
|
||||
= additionals_tag_links(entry.tags, tags_without_color: defined?(tags_without_color) ? tags_without_color : false)
|
||||
'
|
||||
span.contextual
|
||||
= link_to l(:label_edit_tags),
|
||||
{},
|
||||
onclick: "$('#edit_tags_form').show(); $('#tags-data').hide(); return false;",
|
||||
id: 'edit_tags_link'
|
||||
|
||||
#edit_tags_form style="display: none;"
|
||||
= form_tag(update_url, method: :put, multipart: true ) do
|
||||
= render partial: 'tags_form'
|
||||
'
|
||||
= submit_tag l(:button_save), class: 'button-small'
|
||||
'
|
||||
= link_to l(:button_cancel), {}, onclick: "$('#edit_tags_form').hide(); $('#tags-data').show(); return false;"
|
||||
|
||||
- else
|
||||
= additionals_tag_links(entry.tags, tags_without_color: defined?(tags_without_color) ? tags_without_color : false)
|
|
@ -0,0 +1,8 @@
|
|||
- @settings = ActionController::Parameters.new(@settings) unless Rails.version >= '5.2'
|
||||
' Need Help? :
|
||||
= link_to(l(:label_additionals_doc),
|
||||
'https://additionals.readthedocs.io/en/latest/',
|
||||
class: 'external',
|
||||
target: '_blank',
|
||||
rel: 'noopener')
|
||||
= render_tabs additionals_settings_tabs
|
39
plugins/additionals/app/views/additionals/settings/_general.html.slim
Executable file
39
plugins/additionals/app/views/additionals/settings/_general.html.slim
Executable file
|
@ -0,0 +1,39 @@
|
|||
br
|
||||
h3 = l(:label_content_plural)
|
||||
|
||||
p
|
||||
= content_tag(:label, l(:label_account_login))
|
||||
= text_area_tag 'settings[account_login_bottom]', @settings[:account_login_bottom], class: 'wiki-edit', rows: 10
|
||||
em.info
|
||||
= l(:account_login_info)
|
||||
p
|
||||
= content_tag(:label, l(:label_global_sidebar))
|
||||
= text_area_tag 'settings[global_sidebar]', @settings[:global_sidebar], class: 'wiki-edit', rows: 10
|
||||
em.info
|
||||
= l(:global_sidebar_info)
|
||||
p
|
||||
= content_tag(:label, l(:label_global_footer))
|
||||
= text_area_tag 'settings[global_footer]', @settings[:global_footer], class: 'wiki-edit', rows: 5
|
||||
em.info
|
||||
= l(:global_footer_info)
|
||||
|
||||
br
|
||||
h3 = l(:label_setting_plural)
|
||||
p
|
||||
= content_tag(:label, l(:label_external_urls))
|
||||
= select_tag 'settings[external_urls]',
|
||||
options_for_select({ l(:external_url_default) => '0',
|
||||
l(:external_url_new_window) => '1',
|
||||
l(:external_url_noreferrer) => '2' }, @settings['external_urls'])
|
||||
em.info
|
||||
= t(:external_urls_info_html)
|
||||
p
|
||||
= content_tag(:label, l(:label_add_go_to_top))
|
||||
= check_box_tag 'settings[add_go_to_top]', 1, @settings[:add_go_to_top].to_i == 1
|
||||
em.info
|
||||
= t(:add_go_to_top_info)
|
||||
p
|
||||
= content_tag(:label, l(:label_legacy_smiley_support))
|
||||
= check_box_tag 'settings[legacy_smiley_support]', 1, @settings[:legacy_smiley_support].to_i == 1
|
||||
em.info
|
||||
= t(:legacy_smiley_support_info_html)
|
112
plugins/additionals/app/views/additionals/settings/_issues.html.slim
Executable file
112
plugins/additionals/app/views/additionals/settings/_issues.html.slim
Executable file
|
@ -0,0 +1,112 @@
|
|||
br
|
||||
h3 = l(:label_content_plural)
|
||||
|
||||
p
|
||||
= content_tag(:label, l(:label_new_ticket_message))
|
||||
= text_area_tag 'settings[new_ticket_message]', @settings[:new_ticket_message], class: 'wiki-edit', rows: 10
|
||||
em.info = l(:new_ticket_message_info)
|
||||
|
||||
br
|
||||
hr
|
||||
|
||||
h3 = l(:label_setting_plural)
|
||||
.info = t(:top_rules_help)
|
||||
|
||||
br
|
||||
p
|
||||
= content_tag(:label, l(:label_new_issue_on_profile))
|
||||
= check_box_tag 'settings[new_issue_on_profile]', 1, @settings[:new_issue_on_profile].to_i == 1
|
||||
p
|
||||
= content_tag(:label, l(:label_issue_assign_to_me))
|
||||
= check_box_tag 'settings[issue_assign_to_me]', 1, @settings[:issue_assign_to_me].to_i == 1
|
||||
p
|
||||
= content_tag(:label, l(:label_issue_change_status_in_sidebar))
|
||||
= check_box_tag 'settings[issue_change_status_in_sidebar]', 1, @settings[:issue_change_status_in_sidebar].to_i == 1
|
||||
p
|
||||
= content_tag(:label, l(:label_issue_autowatch_involved))
|
||||
= check_box_tag 'settings[issue_autowatch_involved]', 1, @settings[:issue_autowatch_involved].to_i == 1
|
||||
p
|
||||
= content_tag(:label, l(:label_rule_issue_close_with_open_children))
|
||||
= check_box_tag 'settings[issue_close_with_open_children]', 1, @settings[:issue_close_with_open_children].to_i == 1
|
||||
p
|
||||
= content_tag(:label, l(:label_rule_issue_freezed_with_close))
|
||||
= check_box_tag 'settings[issue_freezed_with_close]', 1, @settings[:issue_freezed_with_close].to_i == 1
|
||||
em.info = t(:rule_issue_freezed_with_close_info)
|
||||
|
||||
br
|
||||
|
||||
- rule_status = IssueStatus.sorted
|
||||
p
|
||||
= content_tag(:label, l(:label_rule_issue_status_change))
|
||||
= check_box_tag 'settings[issue_status_change]', 1, @settings[:issue_status_change].to_i == 1
|
||||
span[style="vertical-align: top; margin-left: 15px;"]
|
||||
= l(:field_status)
|
||||
| x:
|
||||
= select_tag 'settings[issue_status_x]',
|
||||
options_for_select(rule_status.collect { |column| [column.name, column.id] },
|
||||
@settings[:issue_status_x]),
|
||||
multiple: true, size: 6, style: 'width:150px'
|
||||
'
|
||||
= l(:field_status)
|
||||
| y:
|
||||
= select_tag 'settings[issue_status_y]',
|
||||
options_for_select(rule_status.collect { |column| [column.name, column.id] },
|
||||
@settings[:issue_status_y]),
|
||||
multiple: false, style: 'width:150px; vertical-align: top'
|
||||
em.info = t(:rule_issue_status_change_info)
|
||||
|
||||
br
|
||||
br
|
||||
p
|
||||
= content_tag(:label, l(:label_rule_issue_current_user_status))
|
||||
= check_box_tag 'settings[issue_current_user_status]', 1, @settings[:issue_current_user_status].to_i == 1
|
||||
span[style="vertical-align: top; margin-left: 15px;"]
|
||||
= l(:field_status)
|
||||
| x:
|
||||
= select_tag 'settings[issue_assign_to_x]',
|
||||
options_for_select(rule_status.collect { |column| [column.name, column.id] },
|
||||
@settings[:issue_assign_to_x]),
|
||||
multiple: true, size: 6, style: 'width:150px'
|
||||
em.info = t(:rule_issue_current_user_status_info_html)
|
||||
|
||||
br
|
||||
br
|
||||
p
|
||||
= content_tag(:label, l(:label_rule_issue_auto_assign))
|
||||
= check_box_tag 'settings[issue_auto_assign]', 1, @settings[:issue_auto_assign].to_i == 1
|
||||
span[style="vertical-align: top; margin-left: 15px;"]
|
||||
= l(:field_status)
|
||||
| x:
|
||||
= select_tag 'settings[issue_auto_assign_status]',
|
||||
options_for_select(rule_status.collect { |column| [column.name, column.id] },
|
||||
@settings[:issue_auto_assign_status]),
|
||||
multiple: true, size: 6, style: 'width:150px'
|
||||
'
|
||||
= l(:label_role)
|
||||
| :
|
||||
= select_tag 'settings[issue_auto_assign_role]',
|
||||
options_from_collection_for_select(Role.givable.sorted, :id, :name, @settings[:issue_auto_assign_role]),
|
||||
multiple: false, style: 'width:150px; vertical-align: top'
|
||||
em.info = t(:rule_issue_auto_assign_info)
|
||||
|
||||
br
|
||||
br
|
||||
p
|
||||
= content_tag(:label, l(:label_rule_issue_timelog_required))
|
||||
= check_box_tag 'settings[issue_timelog_required]', 1, @settings[:issue_timelog_required].to_i == 1
|
||||
span[style="vertical-align: top; margin-left: 15px;"]
|
||||
= l(:label_tracker_plural)
|
||||
| :
|
||||
= select_tag 'settings[issue_timelog_required_tracker]',
|
||||
options_for_select(Tracker.all.sorted.collect { |column| [column.name, column.id] },
|
||||
@settings[:issue_timelog_required_tracker]),
|
||||
multiple: true, size: 6, style: 'width:150px'
|
||||
'
|
||||
= l(:field_status)
|
||||
| :
|
||||
= select_tag 'settings[issue_timelog_required_status]',
|
||||
options_for_select(rule_status.collect { |column| [column.name, column.id] },
|
||||
@settings[:issue_timelog_required_status]),
|
||||
multiple: true, size: 6, style: 'width:150px'
|
||||
em.info
|
||||
= t(:rule_issue_timelog_required_info_html)
|
15
plugins/additionals/app/views/additionals/settings/_macros.html.slim
Executable file
15
plugins/additionals/app/views/additionals/settings/_macros.html.slim
Executable file
|
@ -0,0 +1,15 @@
|
|||
em.info
|
||||
= l(:hidden_macros_in_toolbar_info)
|
||||
|
||||
br
|
||||
|
||||
p
|
||||
= content_tag(:label, l(:label_hidden_macros_in_toolbar))
|
||||
= hidden_field_tag('settings[hidden_macros_in_toolbar][]', '')
|
||||
- @available_macros = AdditionalsMacro.all(only_names: true).each do |m|
|
||||
label.block
|
||||
- value = @settings[:hidden_macros_in_toolbar].present? ? @settings[:hidden_macros_in_toolbar].include?(m) : false
|
||||
= check_box_tag('settings[hidden_macros_in_toolbar][]', m, value, id: nil)
|
||||
= m
|
||||
|
||||
br
|
44
plugins/additionals/app/views/additionals/settings/_menu.html.slim
Executable file
44
plugins/additionals/app/views/additionals/settings/_menu.html.slim
Executable file
|
@ -0,0 +1,44 @@
|
|||
.info = t(:label_top_menu_help_html)
|
||||
br
|
||||
h3 = l(:label_custom_menu_items)
|
||||
|
||||
- 5.times do |i|
|
||||
fieldset
|
||||
legend
|
||||
b = "#{l(:label_menu_entry)} ##{i + 1}"
|
||||
div
|
||||
p
|
||||
label = h l(:field_name)
|
||||
= text_field_tag('settings[custom_menu' + i.to_s + '_name]', @settings['custom_menu' + i.to_s + '_name'], size: 40)
|
||||
p
|
||||
label = h l(:field_url)
|
||||
= text_field_tag('settings[custom_menu' + i.to_s + '_url]', @settings['custom_menu' + i.to_s + '_url'], size: 80)
|
||||
p
|
||||
label = h l(:field_title)
|
||||
= text_field_tag('settings[custom_menu' + i.to_s + '_title]', @settings['custom_menu' + i.to_s + '_title'], size: 80)
|
||||
i
|
||||
| (
|
||||
= l(:label_optional)
|
||||
| )
|
||||
p
|
||||
label = h l(:label_permissions)
|
||||
- permission_field = 'custom_menu' + i.to_s + '_roles'
|
||||
- menu_roles = Struct.new(:id, :name)
|
||||
= select_tag('settings[' + permission_field + ']',
|
||||
options_from_collection_for_select(Role.sorted.collect { |m| menu_roles.new(m.id, m.name) },
|
||||
:id,
|
||||
:name,
|
||||
@settings[permission_field]),
|
||||
multiple: true, style: 'height: 100px;')
|
||||
em.info = l(:menu_roles_info)
|
||||
|
||||
br
|
||||
h3 = l(:label_setting_plural)
|
||||
p
|
||||
= content_tag(:label, l(:label_remove_help))
|
||||
= check_box_tag 'settings[remove_help]', 1, @settings[:remove_help].to_i == 1
|
||||
em.info = l(:remove_help_info)
|
||||
p
|
||||
= content_tag(:label, l(:label_remove_mypage))
|
||||
= check_box_tag 'settings[remove_mypage]', 1, @settings[:remove_mypage].to_i == 1
|
||||
em.info = l(:remove_mypage_info)
|
29
plugins/additionals/app/views/additionals/settings/_overview.html.slim
Executable file
29
plugins/additionals/app/views/additionals/settings/_overview.html.slim
Executable file
|
@ -0,0 +1,29 @@
|
|||
.info = t(:top_overview_help)
|
||||
|
||||
br
|
||||
h3 = l(:label_content_plural)
|
||||
|
||||
p
|
||||
= content_tag(:label, l(:label_overview_right))
|
||||
= text_area_tag 'settings[overview_right]', @settings[:overview_right], class: 'wiki-edit', rows: 10
|
||||
em.info
|
||||
= l(:overview_right_info)
|
||||
p
|
||||
= content_tag(:label, l(:label_overview_top))
|
||||
= text_area_tag 'settings[overview_top]', @settings[:overview_top], class: 'wiki-edit', rows: 10
|
||||
em.info
|
||||
= l(:overview_top_info)
|
||||
p
|
||||
= content_tag(:label, l(:label_overview_bottom))
|
||||
= text_area_tag 'settings[overview_bottom]', @settings[:overview_bottom], class: 'wiki-edit', rows: 10
|
||||
em.info
|
||||
= l(:overview_bottom_info)
|
||||
|
||||
br
|
||||
h3 = l(:label_setting_plural)
|
||||
|
||||
p
|
||||
= content_tag(:label, l(:label_remove_news))
|
||||
= check_box_tag 'settings[remove_news]', 1, @settings[:remove_news].to_i == 1
|
||||
em.info
|
||||
= l(:remove_news_info)
|
26
plugins/additionals/app/views/additionals/settings/_projects.html.slim
Executable file
26
plugins/additionals/app/views/additionals/settings/_projects.html.slim
Executable file
|
@ -0,0 +1,26 @@
|
|||
.info = t(:top_projects_help)
|
||||
br
|
||||
|
||||
p
|
||||
= content_tag(:label, l(:label_project_overview_content))
|
||||
= text_area_tag 'settings[project_overview_content]',
|
||||
@settings[:project_overview_content],
|
||||
class: 'wiki-edit', rows: 10
|
||||
em.info
|
||||
= l(:project_overview_content_info)
|
||||
|
||||
hr
|
||||
|
||||
p
|
||||
= content_tag(:label, l(:label_disabled_modules))
|
||||
= hidden_field_tag('settings[disabled_modules][]', '')
|
||||
- Redmine::AccessControl.available_project_modules_all.each do |m|
|
||||
label.block
|
||||
- value = @settings[:disabled_modules].present? ? @settings[:disabled_modules].include?(m.to_s) : false
|
||||
= check_box_tag('settings[disabled_modules][]', m, value, id: nil)
|
||||
= l_or_humanize(m, prefix: 'project_module_')
|
||||
|
||||
br
|
||||
|
||||
em.info
|
||||
= l(:disabled_modules_info)
|
10
plugins/additionals/app/views/additionals/settings/_users.html.slim
Executable file
10
plugins/additionals/app/views/additionals/settings/_users.html.slim
Executable file
|
@ -0,0 +1,10 @@
|
|||
br
|
||||
h3 = l(:label_user_plural)
|
||||
p
|
||||
= content_tag(:label, l(:label_invisible_captcha))
|
||||
= check_box_tag 'settings[invisible_captcha]',
|
||||
1,
|
||||
@settings[:invisible_captcha].to_i == 1,
|
||||
disabled: (true unless Setting.self_registration?)
|
||||
em.info
|
||||
= t(:invisible_captcha_info_html)
|
7
plugins/additionals/app/views/additionals/settings/_web_apis.html.slim
Executable file
7
plugins/additionals/app/views/additionals/settings/_web_apis.html.slim
Executable file
|
@ -0,0 +1,7 @@
|
|||
br
|
||||
h3 = l(:label_web_apis)
|
||||
p
|
||||
= content_tag(:label, l(:label_google_maps_embed_api))
|
||||
= text_field_tag('settings[google_maps_api_key]', @settings[:google_maps_api_key], size: 60)
|
||||
em.info = t(:google_maps_embed_api_html)
|
||||
= call_hook(:additionals_settings_web_apis, settings: @settings)
|
34
plugins/additionals/app/views/additionals/settings/_wiki.html.slim
Executable file
34
plugins/additionals/app/views/additionals/settings/_wiki.html.slim
Executable file
|
@ -0,0 +1,34 @@
|
|||
.info = t(:top_wiki_help)
|
||||
|
||||
br
|
||||
h3 = l(:label_content_plural)
|
||||
|
||||
p
|
||||
= content_tag(:label, l(:label_global_wiki_sidebar))
|
||||
= text_area_tag 'settings[global_wiki_sidebar]', @settings[:global_wiki_sidebar], class: 'wiki-edit', rows: 10
|
||||
em.info
|
||||
= l(:global_wiki_sidebar_info)
|
||||
p
|
||||
= content_tag(:label, l(:label_global_wiki_header))
|
||||
= text_area_tag 'settings[global_wiki_header]', @settings[:global_wiki_header], class: 'wiki-edit', rows: 5
|
||||
em.info
|
||||
= l(:global_wiki_header_info)
|
||||
p
|
||||
= content_tag(:label, l(:label_global_wiki_footer))
|
||||
= text_area_tag 'settings[global_wiki_footer]', @settings[:global_wiki_footer], class: 'wiki-edit', rows: 5
|
||||
em.info
|
||||
= l(:global_wiki_footer_info)
|
||||
|
||||
br
|
||||
h3 = l(:label_pdf_wiki_settings)
|
||||
|
||||
p
|
||||
= content_tag(:label, l(:label_wiki_pdf_remove_title))
|
||||
= check_box_tag 'settings[wiki_pdf_remove_title]', 1, @settings[:wiki_pdf_remove_title].to_i == 1
|
||||
em.info
|
||||
= l(:wiki_pdf_remove_title_info)
|
||||
p
|
||||
= content_tag(:label, l(:label_wiki_pdf_remove_attachments))
|
||||
= check_box_tag 'settings[wiki_pdf_remove_attachments]', 1, @settings[:wiki_pdf_remove_attachments].to_i == 1
|
||||
em.info
|
||||
= l(:wiki_pdf_remove_attachments_info)
|
11
plugins/additionals/app/views/additionals_macros/show.html.slim
Executable file
11
plugins/additionals/app/views/additionals_macros/show.html.slim
Executable file
|
@ -0,0 +1,11 @@
|
|||
h2 = l(:label_settings_macros) + " (#{@available_macros.count})"
|
||||
|
||||
.info = t(:label_top_macros_help_html)
|
||||
br
|
||||
.box
|
||||
- @available_macros.each do |macro, options|
|
||||
.macro-box
|
||||
.macro-title
|
||||
= macro.to_s
|
||||
.macro-desc
|
||||
pre = options[:desc]
|
11
plugins/additionals/app/views/admin/_system_info.html.slim
Executable file
11
plugins/additionals/app/views/admin/_system_info.html.slim
Executable file
|
@ -0,0 +1,11 @@
|
|||
table.list
|
||||
tr
|
||||
td.name
|
||||
= "#{l(:label_system_info)}:"
|
||||
td.name
|
||||
= system_info
|
||||
tr
|
||||
td.name
|
||||
= "#{l(:label_uptime)}:"
|
||||
td.name
|
||||
= system_uptime
|
|
@ -0,0 +1 @@
|
|||
== @tags.collect { |tag| { 'id' => tag.name, 'text' => tag.name } }.to_json
|
9
plugins/additionals/app/views/auto_completes/_additionals_users.html.erb
Executable file
9
plugins/additionals/app/views/auto_completes/_additionals_users.html.erb
Executable file
|
@ -0,0 +1,9 @@
|
|||
<%= raw @users.map { |user| {
|
||||
'id' => user.id,
|
||||
'text' => user.name,
|
||||
'name' => user.name,
|
||||
'name_with_icon' => user_with_avatar(user, no_link: true),
|
||||
'value' => user.id
|
||||
}
|
||||
}.to_json
|
||||
%>
|
|
@ -0,0 +1,11 @@
|
|||
- if Additionals.setting?(:issue_freezed_with_close) && !User.current.allowed_to?(:edit_closed_issues, project)
|
||||
- if @issues.detect(&:closed?)
|
||||
ruby:
|
||||
@safe_attributes = []
|
||||
@can[:edit] = false
|
||||
@can[:edit] = false
|
||||
@allowed_statuses = nil
|
||||
@trackers = nil
|
||||
@can[:add_watchers] = nil
|
||||
@can[:delete] = nil
|
||||
@options_by_custom_field = []
|
1
plugins/additionals/app/views/hooks/_view_users_contextual.html.slim
Executable file
1
plugins/additionals/app/views/hooks/_view_users_contextual.html.slim
Executable file
|
@ -0,0 +1 @@
|
|||
= call_hook(:view_users_show_contextual, user: @user)
|
1
plugins/additionals/app/views/hooks/_view_users_show.html.slim
Executable file
1
plugins/additionals/app/views/hooks/_view_users_show.html.slim
Executable file
|
@ -0,0 +1 @@
|
|||
= call_hook(:view_users_show_info, user: @user)
|
5
plugins/additionals/app/views/issues/_additionals_action_menu.html.slim
Executable file
5
plugins/additionals/app/views/issues/_additionals_action_menu.html.slim
Executable file
|
@ -0,0 +1,5 @@
|
|||
- if User.current.logged? && @issue.editable? && Additionals.setting?(:issue_assign_to_me) && \
|
||||
@issue.assigned_to_id != User.current.id && @project.assignable_users.detect { |u| u.id == User.current.id }
|
||||
= link_to(font_awesome_icon('far_user-circle', post_text: l(:button_assign_to_me)),
|
||||
issue_assign_to_me_path(@issue), method: :put,
|
||||
class: 'assign-to-me')
|
21
plugins/additionals/app/views/issues/_additionals_sidebar.html.slim
Executable file
21
plugins/additionals/app/views/issues/_additionals_sidebar.html.slim
Executable file
|
@ -0,0 +1,21 @@
|
|||
- if Additionals.setting?(:issue_change_status_in_sidebar) && \
|
||||
@issue && \
|
||||
User.current.allowed_to?(:edit_issues, @project) && \
|
||||
(!@issue.closed? || User.current.allowed_to?(:edit_closed_issues, @project))
|
||||
- statuses = @issue.sidbar_change_status_allowed_to(User.current)
|
||||
- if statuses.present?
|
||||
h3 = l(:label_issue_change_status)
|
||||
ul.issue-status-change-sidebar
|
||||
- statuses.each do |s|
|
||||
- if s != @issue.status
|
||||
li
|
||||
- if s.is_closed?
|
||||
= link_to(font_awesome_icon('fas_caret-square-left', post_text: s.name),
|
||||
issue_change_status_path(@issue, new_status_id: s.id),
|
||||
method: :put, class: "status-switch status-#{s.id}")
|
||||
- else
|
||||
= link_to(font_awesome_icon('far_caret-square-left', post_text: s.name),
|
||||
issue_change_status_path(@issue, new_status_id: s.id),
|
||||
method: :put, class: "status-switch status-#{s.id}")
|
||||
|
||||
h3 = l(:label_planning)
|
9
plugins/additionals/app/views/issues/_change_author.html.slim
Executable file
9
plugins/additionals/app/views/issues/_change_author.html.slim
Executable file
|
@ -0,0 +1,9 @@
|
|||
- if show_issue_change_author?(issue) && issue.safe_attribute?('author_id')
|
||||
- author_options = issue_author_options_for_select(issue.project, issue)
|
||||
- if author_options.present?
|
||||
p#change_author
|
||||
= form.label_for_field :author_id
|
||||
= link_to_function content_tag(:span, l(:button_edit), class: 'icon icon-edit'), '$(this).hide(); $("#issue_author_id").show()'
|
||||
= form.select :author_id, author_options, { required: true, no_label: true }, style: 'display: none'
|
||||
javascript:
|
||||
$('#change_author').insertBefore($('#issue_tracker_id').parent());
|
7
plugins/additionals/app/views/issues/_change_author_bulk.html.slim
Executable file
7
plugins/additionals/app/views/issues/_change_author_bulk.html.slim
Executable file
|
@ -0,0 +1,7 @@
|
|||
- if @project && User.current.allowed_to?(:edit_issue_author, @project)
|
||||
- author_options = issue_author_options_for_select(@project)
|
||||
- if author_options.present?
|
||||
p#change_author
|
||||
= label_tag('issue[author_id]', l(:field_author))
|
||||
= select_tag('issue[author_id]',
|
||||
content_tag('option', l(:label_no_change_option), value: '') + author_options)
|
3
plugins/additionals/app/views/issues/_new_ticket_message.html.slim
Executable file
3
plugins/additionals/app/views/issues/_new_ticket_message.html.slim
Executable file
|
@ -0,0 +1,3 @@
|
|||
- if @issue.new_ticket_message.present?
|
||||
.nodata.nodata-left
|
||||
= textilizable(@issue.new_ticket_message).html_safe
|
4
plugins/additionals/app/views/projects/_project_overview.html.slim
Executable file
4
plugins/additionals/app/views/projects/_project_overview.html.slim
Executable file
|
@ -0,0 +1,4 @@
|
|||
- project_overview_content = Additionals.settings[:project_overview_content]
|
||||
- if project_overview_content.present?
|
||||
.project-content.wiki.box
|
||||
= textilizable(project_overview_content)
|
3
plugins/additionals/app/views/queries/_additionals_description.html.slim
Executable file
3
plugins/additionals/app/views/queries/_additionals_description.html.slim
Executable file
|
@ -0,0 +1,3 @@
|
|||
- if @query.description?
|
||||
.query-description
|
||||
= textilizable @query, :description
|
17
plugins/additionals/app/views/roles/_additionals_form.html.slim
Executable file
17
plugins/additionals/app/views/roles/_additionals_form.html.slim
Executable file
|
@ -0,0 +1,17 @@
|
|||
p
|
||||
= f.check_box :hide, disabled: @role.users_visibility != 'members_of_visible_projects'
|
||||
em.info
|
||||
= t(:info_hidden_roles_html)
|
||||
|
||||
javascript:
|
||||
$(function() {
|
||||
$('#role_users_visibility').change(function() {
|
||||
var uv = $("#role_users_visibility").val();
|
||||
if (uv == 'members_of_visible_projects') {
|
||||
$("#role_hide").prop('disabled', false);
|
||||
} else {
|
||||
$("#role_hide").prop('checked', false);
|
||||
$("#role_hide").prop('disabled', true);
|
||||
}
|
||||
});
|
||||
});
|
4
plugins/additionals/app/views/users/_additionals_contextual.html.slim
Executable file
4
plugins/additionals/app/views/users/_additionals_contextual.html.slim
Executable file
|
@ -0,0 +1,4 @@
|
|||
- if Additionals.setting?(:new_issue_on_profile) && @memberships.present?
|
||||
- project_url = memberships_new_issue_project_url(user, @memberships)
|
||||
- if project_url.present?
|
||||
= link_to(l(:label_issue_new), project_url, class: 'user-new-issue icon icon-add')
|
4
plugins/additionals/app/views/users/_autowatch_involved_issue.html.slim
Executable file
4
plugins/additionals/app/views/users/_autowatch_involved_issue.html.slim
Executable file
|
@ -0,0 +1,4 @@
|
|||
- if Additionals.setting?(:issue_autowatch_involved) && User.current.allowed_to?(:view_issues, nil, global: true)
|
||||
= labelled_fields_for :pref, @user.pref do |pref_fields|
|
||||
p
|
||||
= pref_fields.check_box :autowatch_involved_issue
|
5
plugins/additionals/app/views/welcome/_overview_bottom.html.slim
Executable file
5
plugins/additionals/app/views/welcome/_overview_bottom.html.slim
Executable file
|
@ -0,0 +1,5 @@
|
|||
- overview_bottom = Additionals.settings[:overview_bottom]
|
||||
- if overview_bottom.present?
|
||||
.clear-both
|
||||
.overview-bottom.wiki.box
|
||||
= textilizable(overview_bottom)
|
5
plugins/additionals/app/views/welcome/_overview_news.html.slim
Executable file
5
plugins/additionals/app/views/welcome/_overview_news.html.slim
Executable file
|
@ -0,0 +1,5 @@
|
|||
- unless Additionals.setting?(:remove_news)
|
||||
.news.box
|
||||
h3 = l(:label_news_latest)
|
||||
= render partial: 'news/news', collection: @news
|
||||
= link_to l(:label_news_view_all), news_index_path
|
4
plugins/additionals/app/views/welcome/_overview_right.html.slim
Executable file
4
plugins/additionals/app/views/welcome/_overview_right.html.slim
Executable file
|
@ -0,0 +1,4 @@
|
|||
- overview_right = Additionals.settings[:overview_right]
|
||||
- if overview_right.present?
|
||||
.overview-right.wiki.box
|
||||
= textilizable(overview_right)
|
4
plugins/additionals/app/views/welcome/_overview_top.html.slim
Executable file
4
plugins/additionals/app/views/welcome/_overview_top.html.slim
Executable file
|
@ -0,0 +1,4 @@
|
|||
- overview_top = Additionals.settings[:overview_top]
|
||||
- if overview_top.present?
|
||||
.overview-top.wiki.box
|
||||
= textilizable(overview_top)
|
17
plugins/additionals/app/views/wiki/_calendar_macros.html.slim
Executable file
17
plugins/additionals/app/views/wiki/_calendar_macros.html.slim
Executable file
|
@ -0,0 +1,17 @@
|
|||
.month-calendar id="month-calendar-#{id}"
|
||||
javascript:
|
||||
$("#month-calendar-#{id}").datepicker({
|
||||
language: "#{locale}",
|
||||
calendarWeeks: #{options[:show_weeks]},
|
||||
todayHighlight: true,
|
||||
multidate: true,
|
||||
disableTouchKeyboard: true,
|
||||
defaultViewDate: {
|
||||
year: #{options[:year]},
|
||||
month: #{options[:month]},
|
||||
day: 1
|
||||
}
|
||||
});
|
||||
- unless selected.empty?
|
||||
javascript:
|
||||
$('#month-calendar-#{id}').datepicker('setDates', #{selected});
|
16
plugins/additionals/app/views/wiki/_cryptocompare.html.slim
Executable file
16
plugins/additionals/app/views/wiki/_cryptocompare.html.slim
Executable file
|
@ -0,0 +1,16 @@
|
|||
.cryptocompare
|
||||
javascript:
|
||||
var scripts = document.getElementsByTagName("script");
|
||||
var embedder = scripts[scripts.length - 1];
|
||||
(function() {
|
||||
var appName = encodeURIComponent(window.location.hostname);
|
||||
if (appName == "") {
|
||||
appName = "local";
|
||||
}
|
||||
var s = document.createElement("script");
|
||||
s.type = "text/javascript";
|
||||
s.async = true;
|
||||
var theUrl = "#{raw url}";
|
||||
s.src = theUrl + (theUrl.indexOf("?") >= 0 ? "&" : "?") + "app=" + appName;
|
||||
embedder.parentNode.appendChild(s);
|
||||
})();
|
5
plugins/additionals/app/views/wiki/_global_sidebar.html.slim
Executable file
5
plugins/additionals/app/views/wiki/_global_sidebar.html.slim
Executable file
|
@ -0,0 +1,5 @@
|
|||
- sidebar = Additionals.settings[:global_sidebar]
|
||||
- if sidebar.present?
|
||||
.sidebar-additionals
|
||||
= textilizable(sidebar)
|
||||
br
|
15
plugins/additionals/app/views/wiki/_project_macros.html.slim
Executable file
15
plugins/additionals/app/views/wiki/_project_macros.html.slim
Executable file
|
@ -0,0 +1,15 @@
|
|||
.additionals-projects.box
|
||||
- if list_title
|
||||
h3 = list_title
|
||||
ul
|
||||
- @projects.each do |project|
|
||||
li.project class="#{cycle('odd', 'even')}"
|
||||
span[style='font-weight: bold;']
|
||||
= link_to_project(project)
|
||||
- if project.homepage?
|
||||
' :
|
||||
= link_to(project.homepage, project.homepage, @html_options)
|
||||
- if with_create_issue && User.current.allowed_to?(:add_issues, project)
|
||||
= link_to('',
|
||||
new_project_issue_path(project_id: project),
|
||||
class: 'icon icon-add', title: l(:label_issue_new))
|
4
plugins/additionals/app/views/wiki/_show_additionals.html.slim
Executable file
4
plugins/additionals/app/views/wiki/_show_additionals.html.slim
Executable file
|
@ -0,0 +1,4 @@
|
|||
- content_for :header_tags do
|
||||
= stylesheet_link_tag 'bootstrap-datepicker3.standalone.min', plugin: 'additionals'
|
||||
= javascript_include_tag('bootstrap-datepicker.min', plugin: 'additionals')
|
||||
= bootstrap_datepicker_locale
|
6
plugins/additionals/app/views/wiki/_tradingview.html.slim
Executable file
6
plugins/additionals/app/views/wiki/_tradingview.html.slim
Executable file
|
@ -0,0 +1,6 @@
|
|||
/! TradingView Widget BEGIN
|
||||
= javascript_include_tag 'https://s3.tradingview.com/tv.js'
|
||||
div[style="display: inline-block;"]
|
||||
javascript:
|
||||
new TradingView.widget(#{raw options.to_json});
|
||||
/! TradingView Widget END
|
23
plugins/additionals/app/views/wiki/_user_macros.html.slim
Executable file
23
plugins/additionals/app/views/wiki/_user_macros.html.slim
Executable file
|
@ -0,0 +1,23 @@
|
|||
.users.box
|
||||
- if list_title
|
||||
h3 = list_title
|
||||
- users.each do |user|
|
||||
.user.box class="#{cycle('odd', 'even')}"
|
||||
div[style="float: left; display: block; margin-right: 5px;"]
|
||||
= avatar(user, size: 50)
|
||||
.user.line[style="font-weight: bold;"]
|
||||
= link_to_user(user)
|
||||
- if !user_roles.nil? && user_roles[user.id]
|
||||
.user.line
|
||||
= l(:field_role)
|
||||
' :
|
||||
= user_roles[user.id].join(', ').html_safe
|
||||
.user.line
|
||||
= l(:field_login)
|
||||
' :
|
||||
= link_to user.login, '/users/' + user.id.to_s
|
||||
- unless user.pref.hide_mail
|
||||
.user.line
|
||||
= l(:field_mail)
|
||||
' :
|
||||
= mail_to(user.mail, nil, encode: 'javascript')
|
Loading…
Add table
Add a link
Reference in a new issue