Actualiza el plugin Additionals a 3.0.2-master
This commit is contained in:
parent
3b6a41320c
commit
cfa0d58b18
164 changed files with 2027 additions and 58190 deletions
|
@ -13,7 +13,7 @@ class AdditionalsChangeStatusController < ApplicationController
|
|||
return
|
||||
end
|
||||
|
||||
@issue.init_journal(User.current)
|
||||
@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
|
||||
|
||||
|
|
|
@ -6,7 +6,6 @@ class DashboardAsyncBlocksController < ApplicationController
|
|||
|
||||
helper :additionals_routes
|
||||
helper :additionals_queries
|
||||
helper :additionals_tag
|
||||
helper :queries
|
||||
helper :issues
|
||||
helper :activities
|
||||
|
@ -14,6 +13,12 @@ class DashboardAsyncBlocksController < ApplicationController
|
|||
|
||||
include DashboardsHelper
|
||||
|
||||
# support for redmine_contacts_helpdesk plugin
|
||||
if Redmine::Plugin.installed? 'redmine_contacts_helpdesk'
|
||||
include HelpdeskHelper
|
||||
helper :helpdesk
|
||||
end
|
||||
|
||||
rescue_from Query::StatementInvalid, with: :query_statement_invalid
|
||||
rescue_from StandardError, with: :dashboard_with_invalid_block
|
||||
|
||||
|
|
|
@ -17,7 +17,6 @@ class DashboardsController < ApplicationController
|
|||
helper :dashboards
|
||||
helper :additionals_issues
|
||||
helper :additionals_queries
|
||||
helper :additionals_tag
|
||||
|
||||
include AdditionalsRoutesHelper
|
||||
include AdditionalsQueriesHelper
|
||||
|
@ -92,7 +91,7 @@ class DashboardsController < ApplicationController
|
|||
|
||||
respond_to do |format|
|
||||
format.html
|
||||
format.xml {}
|
||||
format.api
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ module AdditionalsChartjsHelper
|
|||
end
|
||||
|
||||
def select_options_for_chartjs_colorscheme(selected)
|
||||
data = YAML.safe_load(ERB.new(IO.read(Rails.root.join('plugins/additionals/config/colorschemes.yml'))).result) || {}
|
||||
data = YAML.safe_load(ERB.new(IO.read(File.join(Additionals.plugin_dir, 'config', 'colorschemes.yml'))).result) || {}
|
||||
grouped_options_for_select(data, selected)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -3,20 +3,34 @@ module AdditionalsClipboardjsHelper
|
|||
render_clipboardjs_button(target, clipboard_text_from_button) + render_clipboardjs_javascript(target)
|
||||
end
|
||||
|
||||
def render_text_with_clipboardjs(text)
|
||||
return if text.blank?
|
||||
|
||||
tag.acronym text,
|
||||
class: 'clipboard-text',
|
||||
title: l(:label_copy_to_clipboard),
|
||||
data: clipboardjs_data(text: text)
|
||||
end
|
||||
|
||||
def clipboardjs_data(clipboard_data)
|
||||
data = { 'label-copied' => l(:label_copied_to_clipboard),
|
||||
'label-to-copy' => l(:label_copy_to_clipboard) }
|
||||
|
||||
clipboard_data.each do |key, value|
|
||||
data["clipboard-#{key}"] = value if value.present?
|
||||
end
|
||||
|
||||
data
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def render_clipboardjs_button(target, clipboard_text_from_button)
|
||||
data = { 'clipboard-target' => "##{target}",
|
||||
'label-copied' => l(:label_copied_to_clipboard),
|
||||
'label-to-copy' => l(:label_copy_to_clipboard) }
|
||||
|
||||
data['clipboard-text'] = clipboard_text_from_button if clipboard_text_from_button.present?
|
||||
|
||||
tag.button id: "zc_#{target}",
|
||||
class: 'clipboard_button far fa-copy',
|
||||
class: 'clipboard-button far fa-copy',
|
||||
type: 'button',
|
||||
title: l(:label_copy_to_clipboard),
|
||||
data: data
|
||||
data: clipboardjs_data(target: "##{target}", text: clipboard_text_from_button)
|
||||
end
|
||||
|
||||
def render_clipboardjs_javascript(target)
|
||||
|
|
|
@ -100,12 +100,9 @@ module AdditionalsMenuHelper
|
|||
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
|
||||
if user_roles.empty? && role.to_i == Role::BUILTIN_ANONYMOUS ||
|
||||
# if user is logged in and non_member is active in item, always show it
|
||||
User.current.logged? && role.to_i == Role::BUILTIN_NON_MEMBER
|
||||
show_entry = true
|
||||
break
|
||||
end
|
||||
|
|
|
@ -4,8 +4,8 @@ module AdditionalsQueriesHelper
|
|||
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")
|
||||
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? ||
|
||||
|
@ -28,7 +28,7 @@ module AdditionalsQueriesHelper
|
|||
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))
|
||||
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],
|
||||
|
@ -79,11 +79,11 @@ module AdditionalsQueriesHelper
|
|||
def additionals_select2_search_users(options = {})
|
||||
q = params[:q].to_s.strip
|
||||
exclude_id = params[:user_id].to_i
|
||||
scope = User.active.where(type: 'User')
|
||||
scope = User.active.where type: 'User'
|
||||
scope = scope.visible unless options[:all_visible]
|
||||
scope = scope.where.not(id: exclude_id) if exclude_id.positive?
|
||||
scope = scope.where(options[:where_filter], options[:where_params]) if options[:where_filter]
|
||||
q.split(' ').map { |search_string| scope = scope.like(search_string) } if q.present?
|
||||
q.split.map { |search_string| scope = scope.like(search_string) } if q.present?
|
||||
scope = scope.order(last_login_on: :desc)
|
||||
.limit(Additionals::SELECT2_INIT_ENTRIES)
|
||||
@users = scope.to_a.sort! { |x, y| x.name <=> y.name }
|
||||
|
@ -140,13 +140,13 @@ module AdditionalsQueriesHelper
|
|||
def xlsx_write_header_row(workbook, worksheet, columns)
|
||||
columns_width = []
|
||||
columns.each_with_index do |c, index|
|
||||
value = if c.class.name == 'String'
|
||||
value = if c.is_a? String
|
||||
c
|
||||
else
|
||||
c.caption.to_s
|
||||
end
|
||||
|
||||
worksheet.write(0, index, value, workbook.add_format(xlsx_cell_format(:header)))
|
||||
worksheet.write 0, index, value, workbook.add_format(xlsx_cell_format(:header))
|
||||
columns_width << xlsx_get_column_width(value)
|
||||
end
|
||||
columns_width
|
||||
|
@ -224,13 +224,11 @@ module AdditionalsQueriesHelper
|
|||
|
||||
def xlsx_hyperlink_cell?(token)
|
||||
# Match http, https or ftp URL
|
||||
if %r{\A[fh]tt?ps?://}.match?(token)
|
||||
true
|
||||
# Match mailto:
|
||||
elsif token.present? && token.start_with?('mailto:')
|
||||
true
|
||||
# Match internal or external sheet link
|
||||
elsif /\A(?:in|ex)ternal:/.match?(token)
|
||||
if %r{\A[fh]tt?ps?://}.match?(token) ||
|
||||
# Match mailto:
|
||||
token.present? && token.start_with?('mailto:') ||
|
||||
# Match internal or external sheet link
|
||||
/\A(?:in|ex)ternal:/.match?(token)
|
||||
true
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
module AdditionalsSelect2Helper
|
||||
def additionals_select2_tag(name, option_tags = nil, options = {})
|
||||
s = select_tag(name, option_tags, options)
|
||||
id = options.delete(:id) || sanitize_to_id(name)
|
||||
s << hidden_field_tag("#{name}[]", '') if options[:multiple] && options.fetch(:include_hidden, true)
|
||||
|
||||
s + javascript_tag("select2Tag('#{sanitize_to_id name}', #{options.to_json});")
|
||||
s + javascript_tag("select2Tag('#{id}', #{options.to_json});")
|
||||
end
|
||||
|
||||
# Transforms select filters of +type+ fields into select2
|
||||
|
|
|
@ -4,7 +4,6 @@ module AdditionalsSettingsHelper
|
|||
{ name: 'wiki', partial: 'additionals/settings/wiki', label: :label_wiki },
|
||||
{ name: 'macros', partial: 'additionals/settings/macros', label: :label_macro_plural },
|
||||
{ name: 'rules', partial: 'additionals/settings/issues', label: :label_issue_plural },
|
||||
{ name: 'users', partial: 'additionals/settings/users', label: :label_user_plural },
|
||||
{ name: 'web', partial: 'additionals/settings/web_apis', label: :label_web_apis }]
|
||||
|
||||
unless Redmine::Plugin.installed? 'redmine_hrm'
|
||||
|
@ -46,6 +45,22 @@ module AdditionalsSettingsHelper
|
|||
text_field_tag("settings[#{name}]", value, options)]
|
||||
end
|
||||
|
||||
def additionals_settings_passwordfield(name, options = {})
|
||||
label_title = options.delete(:label).presence || l("label_#{name}")
|
||||
value = options.delete(:value).presence || @settings[name]
|
||||
|
||||
safe_join [label_tag("settings[#{name}]", label_title),
|
||||
password_field_tag("settings[#{name}]", value, options)]
|
||||
end
|
||||
|
||||
def additionals_settings_urlfield(name, options = {})
|
||||
label_title = options.delete(:label).presence || l("label_#{name}")
|
||||
value = options.delete(:value).presence || @settings[name]
|
||||
|
||||
safe_join [label_tag("settings[#{name}]", label_title),
|
||||
url_field_tag("settings[#{name}]", value, options)]
|
||||
end
|
||||
|
||||
def additionals_settings_textarea(name, options = {})
|
||||
label_title = options.delete(:label).presence || l("label_#{name}")
|
||||
value = options.delete(:value).presence || @settings[name]
|
||||
|
|
|
@ -1,113 +0,0 @@
|
|||
require 'digest/md5'
|
||||
|
||||
module AdditionalsTagHelper
|
||||
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
|
||||
|
||||
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_object, options = {})
|
||||
tag_name = []
|
||||
tag_name << tag_object.name
|
||||
|
||||
unless options[:tags_without_color]
|
||||
tag_bg_color = additionals_tag_color tag_object.name
|
||||
tag_fg_color = additionals_tag_fg_color tag_bg_color
|
||||
tag_style = "background-color: #{tag_bg_color}; color: #{tag_fg_color}"
|
||||
end
|
||||
|
||||
tag_name << tag.span("(#{tag_object.count})", class: 'tag-count') if options[:show_count]
|
||||
|
||||
if options[:tags_without_color]
|
||||
tag.span link_to(safe_join(tag_name), additionals_tag_url(tag_object.name, options)),
|
||||
class: 'tag-label'
|
||||
else
|
||||
tag.span link_to(safe_join(tag_name),
|
||||
additionals_tag_url(tag_object.name, options),
|
||||
style: tag_style),
|
||||
class: 'additionals-tag-label-color',
|
||||
style: tag_style
|
||||
end
|
||||
end
|
||||
|
||||
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: options[: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
|
|
@ -86,10 +86,10 @@ module DashboardsHelper
|
|||
tag.div class: 'active-dashboards' do
|
||||
out = [tag.h3(l(:label_active_dashboard)),
|
||||
tag.ul do
|
||||
concat tag.ul "#{l :field_name}: #{dashboard.name}"
|
||||
concat tag.ul safe_join([l(:field_author), link_to_user(dashboard.author)], ': ')
|
||||
concat tag.ul "#{l :field_created_on}: #{format_time dashboard.created_at}"
|
||||
concat tag.ul "#{l :field_updated_on}: #{format_time dashboard.updated_at}"
|
||||
concat tag.li "#{l :field_name}: #{dashboard.name}"
|
||||
concat tag.li safe_join([l(:field_author), link_to_user(dashboard.author)], ': ')
|
||||
concat tag.li "#{l :field_created_on}: #{format_time dashboard.created_at}"
|
||||
concat tag.li "#{l :field_updated_on}: #{format_time dashboard.updated_at}"
|
||||
end]
|
||||
|
||||
if dashboard.description.present?
|
||||
|
@ -105,7 +105,7 @@ module DashboardsHelper
|
|||
return '' unless dashboards.any?
|
||||
|
||||
tag.h3(title, class: 'dashboards') +
|
||||
tag.ul do
|
||||
tag.ul(class: 'dashboards') do
|
||||
dashboards.each do |dashboard|
|
||||
selected = dashboard.id == if params[:dashboard_id].present?
|
||||
params[:dashboard_id].to_i
|
||||
|
@ -115,19 +115,24 @@ module DashboardsHelper
|
|||
|
||||
css = 'dashboard'
|
||||
css << ' selected' if selected
|
||||
li_class = nil
|
||||
|
||||
link = [dashboard_link(dashboard, project, class: css)]
|
||||
if dashboard.system_default?
|
||||
link << if dashboard.project_id.nil?
|
||||
font_awesome_icon('fas_cube',
|
||||
li_class = 'global'
|
||||
font_awesome_icon 'fas_cube',
|
||||
title: l(:field_system_default),
|
||||
class: 'dashboard-system-default global')
|
||||
class: "dashboard-system-default #{li_class}"
|
||||
else
|
||||
font_awesome_icon('fas_cube',
|
||||
li_class = 'project'
|
||||
font_awesome_icon 'fas_cube',
|
||||
title: l(:field_project_system_default),
|
||||
class: 'dashboard-system-default project')
|
||||
class: "dashboard-system-default #{li_class}"
|
||||
end
|
||||
end
|
||||
concat tag.li safe_join(link)
|
||||
|
||||
concat tag.li safe_join(link), class: li_class
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -325,7 +330,7 @@ module DashboardsHelper
|
|||
max_entries = settings[:max_entries] || DashboardContent::DEFAULT_MAX_ENTRIES
|
||||
|
||||
news = if dashboard.content_project.nil?
|
||||
News.latest User.current
|
||||
News.latest User.current, max_entries
|
||||
else
|
||||
dashboard.content_project
|
||||
.news
|
||||
|
@ -378,7 +383,7 @@ module DashboardsHelper
|
|||
max_entries = max_entries.present? ? max_entries.to_i : 10
|
||||
|
||||
begin
|
||||
URI.open(url) do |rss_feed|
|
||||
URI.parse(url).open do |rss_feed|
|
||||
rss = RSS::Parser.parse(rss_feed)
|
||||
rss.items.each do |item|
|
||||
cnt += 1
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
class AdditionalsRemoveUnusedTagJob < AdditionalsJob
|
||||
def perform
|
||||
AdditionalsTag.remove_unused_tags
|
||||
end
|
||||
end
|
|
@ -1,4 +1,5 @@
|
|||
class AdditionalsChart < ActiveRecord::Base
|
||||
class AdditionalsChart
|
||||
include ActiveRecord::Sanitization
|
||||
include Redmine::I18n
|
||||
|
||||
CHART_DEFAULT_HEIGHT = 350
|
||||
|
|
|
@ -6,7 +6,7 @@ class AdditionalsFontAwesome
|
|||
|
||||
class << self
|
||||
def load_icons(type)
|
||||
data = YAML.safe_load(ERB.new(IO.read(Rails.root.join('plugins/additionals/config/fontawesome_icons.yml'))).result) || {}
|
||||
data = YAML.safe_load(ERB.new(IO.read(File.join(Additionals.plugin_dir, '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))
|
||||
|
|
|
@ -31,7 +31,7 @@ class AdditionalsImport < Import
|
|||
next unless value
|
||||
|
||||
h[v.custom_field.id.to_s] =
|
||||
if value.is_a?(Array)
|
||||
if value.is_a? Array
|
||||
value.map { |val| v.custom_field.value_from_keyword(val.strip, object) }.flatten!&.compact
|
||||
else
|
||||
v.custom_field.value_from_keyword(value, object)
|
||||
|
|
73
plugins/additionals/app/models/additionals_info.rb
Normal file
73
plugins/additionals/app/models/additionals_info.rb
Normal file
|
@ -0,0 +1,73 @@
|
|||
class AdditionalsInfo
|
||||
include Redmine::I18n
|
||||
|
||||
class << self
|
||||
def system_infos
|
||||
{ system_info: { label: l(:label_system_info), value: system_info },
|
||||
system_uptime: { label: l(:label_uptime), value: system_uptime } }
|
||||
end
|
||||
|
||||
def system_info
|
||||
if windows_platform?
|
||||
win_info = `wmic os get Caption,CSDVersion,BuildNumber /value`
|
||||
return 'unknown' if win_info.blank?
|
||||
|
||||
windows_version = ''
|
||||
windows_build = ''
|
||||
build_names = %w[BuildNumber CSDVersion]
|
||||
win_info.split(/\n+/).each do |line|
|
||||
line_info = line.split '='
|
||||
if line_info[0] == 'Caption'
|
||||
windows_version = line_info[1]
|
||||
elsif build_names.include?(line_info[0]) && line_info[1]&.present?
|
||||
windows_build = line_info[1]
|
||||
end
|
||||
end
|
||||
"#{windows_version} build #{windows_build}"
|
||||
else
|
||||
`uname -a`
|
||||
end
|
||||
end
|
||||
|
||||
def system_uptime(format: :time_tag)
|
||||
if windows_platform?
|
||||
`net stats srv | find "Statist"`
|
||||
elsif File.exist? '/proc/uptime'
|
||||
secs = `cat /proc/uptime`.to_i
|
||||
min = 0
|
||||
hours = 0
|
||||
days = 0
|
||||
if secs.positive?
|
||||
min = (secs / 60).round
|
||||
hours = (secs / 3_600).round
|
||||
days = (secs / 86_400).round
|
||||
end
|
||||
if days >= 1
|
||||
"#{days} #{l(:days, count: days)}"
|
||||
elsif hours >= 1
|
||||
"#{hours} #{l(:hours, count: hours)}"
|
||||
else
|
||||
"#{min} #{l(:minutes, count: min)}"
|
||||
end
|
||||
else
|
||||
# this should be work on macOS
|
||||
seconds = `sysctl -n kern.boottime | awk '{print $4}'`.tr ',', ''
|
||||
so = DateTime.strptime seconds.strip, '%s'
|
||||
if so.present?
|
||||
if format == :datetime
|
||||
so
|
||||
else
|
||||
ApplicationController.helpers.time_tag so
|
||||
end
|
||||
else
|
||||
days = `uptime | awk '{print $3}'`.to_i.round
|
||||
"#{days} #{l(:days, count: days)}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def windows_platform?
|
||||
/cygwin|mswin|mingw|bccwin|wince|emx/.match? RUBY_PLATFORM
|
||||
end
|
||||
end
|
||||
end
|
|
@ -22,6 +22,17 @@ module AdditionalsQuery
|
|||
sql.join(' AND ')
|
||||
end
|
||||
|
||||
def fix_sql_for_text_field(field, operator, value, table_name = nil, target_field = nil)
|
||||
table_name = queried_table_name if table_name.blank?
|
||||
target_field = field if target_field.blank?
|
||||
|
||||
sql = []
|
||||
sql << "(#{sql_for_field(field, operator, value, table_name, target_field)})"
|
||||
sql << "#{table_name}.#{target_field} != ''" if operator == '*'
|
||||
|
||||
sql.join(' AND ')
|
||||
end
|
||||
|
||||
def initialize_ids_filter(options = {})
|
||||
if options[:label]
|
||||
add_available_filter 'ids', type: :integer, label: options[:label]
|
||||
|
@ -44,30 +55,44 @@ module AdditionalsQuery
|
|||
end
|
||||
end
|
||||
|
||||
def sql_for_project_identifier_field(field, operator, values)
|
||||
value = values.first
|
||||
values = value.split(',').map(&:strip) if ['=', '!'].include?(operator) && value.include?(',')
|
||||
sql_for_field field, operator, values, Project.table_name, 'identifier'
|
||||
end
|
||||
|
||||
def sql_for_project_status_field(field, operator, value)
|
||||
sql_for_field field, operator, value, Project.table_name, 'status'
|
||||
end
|
||||
|
||||
def initialize_project_status_filter
|
||||
return if project&.leaf?
|
||||
def initialize_project_identifier_filter
|
||||
return if project
|
||||
|
||||
add_available_filter('project.status',
|
||||
add_available_filter 'project.identifier',
|
||||
type: :string,
|
||||
name: l(:label_attribute_of_project, name: l(:field_identifier))
|
||||
end
|
||||
|
||||
def initialize_project_status_filter
|
||||
return if project
|
||||
|
||||
add_available_filter 'project.status',
|
||||
type: :list,
|
||||
name: l(:label_attribute_of_project, name: l(:field_status)),
|
||||
values: -> { project_statuses_values })
|
||||
values: -> { project_statuses_values }
|
||||
end
|
||||
|
||||
def initialize_project_filter(options = {})
|
||||
if project.nil? || options[:always]
|
||||
add_available_filter('project_id', order: options[:position],
|
||||
add_available_filter 'project_id', order: options[:position],
|
||||
type: :list,
|
||||
values: -> { project_values })
|
||||
values: -> { project_values }
|
||||
end
|
||||
return if project.nil? || project.leaf? || subproject_values.empty?
|
||||
|
||||
add_available_filter('subproject_id', order: options[:position],
|
||||
add_available_filter 'subproject_id', order: options[:position],
|
||||
type: :list_subprojects,
|
||||
values: -> { subproject_values })
|
||||
values: -> { subproject_values }
|
||||
end
|
||||
|
||||
def initialize_created_filter(options = {})
|
||||
|
@ -83,16 +108,9 @@ module AdditionalsQuery
|
|||
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] }
|
||||
type: :list_optional,
|
||||
values: -> { tag_values(project) }
|
||||
end
|
||||
|
||||
def initialize_approved_filter
|
||||
|
@ -106,27 +124,35 @@ module AdditionalsQuery
|
|||
end
|
||||
|
||||
def initialize_author_filter(options = {})
|
||||
return if author_values.empty?
|
||||
|
||||
add_available_filter('author_id', order: options[:position],
|
||||
add_available_filter 'author_id', order: options[:position],
|
||||
type: :list_optional,
|
||||
values: options[:no_lambda].nil? ? author_values : -> { author_values })
|
||||
values: -> { author_values }
|
||||
end
|
||||
|
||||
def initialize_assignee_filter(options = {})
|
||||
return if author_values.empty?
|
||||
|
||||
add_available_filter('assigned_to_id', order: options[:position],
|
||||
add_available_filter 'assigned_to_id', order: options[:position],
|
||||
type: :list_optional,
|
||||
values: options[:no_lambda] ? assigned_to_all_values : -> { assigned_to_all_values })
|
||||
values: -> { assigned_to_all_values }
|
||||
end
|
||||
|
||||
def initialize_watcher_filter(options = {})
|
||||
return if watcher_values.empty? || !User.current.logged?
|
||||
return unless User.current.logged?
|
||||
|
||||
add_available_filter('watcher_id', order: options[:position],
|
||||
add_available_filter 'watcher_id', order: options[:position],
|
||||
type: :list,
|
||||
values: options[:no_lambda] ? watcher_values : -> { watcher_values })
|
||||
values: -> { watcher_values }
|
||||
end
|
||||
|
||||
def tag_values(project)
|
||||
values = if project
|
||||
queried_class.available_tags project: project.id
|
||||
else
|
||||
queried_class.available_tags
|
||||
end
|
||||
|
||||
return [] if values.blank?
|
||||
|
||||
values.collect { |t| [t.name, t.name] }
|
||||
end
|
||||
|
||||
# issue independend values. Use assigned_to_values from Redmine, if you want it only for issues
|
||||
|
@ -154,7 +180,7 @@ module AdditionalsQuery
|
|||
end
|
||||
|
||||
def sql_for_tags_field(field, _operator, value)
|
||||
AdditionalsTag.sql_for_tags_field(queried_class, operator_for(field), value)
|
||||
AdditionalTags.sql_for_tags_field queried_class, operator_for(field), value
|
||||
end
|
||||
|
||||
def sql_for_is_private_field(_field, operator, value)
|
||||
|
|
|
@ -1,76 +0,0 @@
|
|||
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
|
||||
|
||||
class << self
|
||||
def all_type_tags(klass, options = {})
|
||||
RedmineCrm::Tag.where({})
|
||||
.joins(tag_joins(klass, options))
|
||||
.distinct
|
||||
.order("#{TAG_TABLE_NAME}.name")
|
||||
end
|
||||
|
||||
def 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.select("#{TAG_TABLE_NAME}.*, COUNT(DISTINCT #{TAGGING_TABLE_NAME}.taggable_id) AS count")
|
||||
.joins(tag_joins(klass, options))
|
||||
.group("#{TAG_TABLE_NAME}.id, #{TAG_TABLE_NAME}.name").having('COUNT(*) > 0')
|
||||
.order("#{TAG_TABLE_NAME}.name")
|
||||
end
|
||||
|
||||
def remove_unused_tags
|
||||
RedmineCrm::Tag.where.not(id: RedmineCrm::Tagging.select(:tag_id).distinct)
|
||||
.each(&:destroy)
|
||||
end
|
||||
|
||||
def 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
|
||||
|
||||
private
|
||||
|
||||
def tag_access(permission)
|
||||
projects_allowed = if permission.nil?
|
||||
Project.visible.ids
|
||||
else
|
||||
Project.where(Project.allowed_to_condition(User.current, permission)).ids
|
||||
end
|
||||
|
||||
if projects_allowed.present?
|
||||
"#{PROJECT_TABLE_NAME}.id IN (#{projects_allowed.join ','})" unless projects_allowed.empty?
|
||||
else
|
||||
'1=0'
|
||||
end
|
||||
end
|
||||
|
||||
def 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
|
||||
end
|
||||
end
|
|
@ -4,6 +4,7 @@ class Dashboard < ActiveRecord::Base
|
|||
include Additionals::EntityMethods
|
||||
|
||||
class SystemDefaultChangeException < StandardError; end
|
||||
|
||||
class ProjectSystemDefaultChangeException < StandardError; end
|
||||
|
||||
belongs_to :project
|
||||
|
@ -302,7 +303,10 @@ class Dashboard < ActiveRecord::Base
|
|||
config = { dashboard_id: id,
|
||||
block: block }
|
||||
|
||||
settings[:user_id] = User.current.id if !options.key?(:skip_user_id) || !options[:skip_user_id]
|
||||
if !options.key?(:skip_user_id) || !options[:skip_user_id]
|
||||
settings[:user_id] = User.current.id
|
||||
settings[:user_is_admin] = User.current.admin?
|
||||
end
|
||||
|
||||
if settings.present?
|
||||
settings.each do |key, setting|
|
||||
|
@ -321,11 +325,11 @@ class Dashboard < ActiveRecord::Base
|
|||
unique_params = settings.flatten
|
||||
unique_params += options[:unique_params].reject(&:blank?) if options[:unique_params].present?
|
||||
|
||||
Rails.logger.debug "debug async_params for #{block}: unique_params=#{unique_params.inspect}"
|
||||
# Rails.logger.debug "debug async_params for #{block}: unique_params=#{unique_params.inspect}"
|
||||
config[:unique_key] = Digest::SHA256.hexdigest(unique_params.join('_'))
|
||||
end
|
||||
|
||||
Rails.logger.debug "debug async_params for #{block}: config=#{config.inspect}"
|
||||
# Rails.logger.debug "debug async_params for #{block}: config=#{config.inspect}"
|
||||
|
||||
config
|
||||
end
|
||||
|
@ -358,7 +362,10 @@ class Dashboard < ActiveRecord::Base
|
|||
end
|
||||
|
||||
def validate_system_default
|
||||
return if new_record? || system_default_was == system_default || system_default?
|
||||
return if new_record? ||
|
||||
system_default_was == system_default ||
|
||||
system_default? ||
|
||||
project_id.present?
|
||||
|
||||
raise SystemDefaultChangeException
|
||||
end
|
||||
|
|
|
@ -41,9 +41,13 @@ class DashboardContent
|
|||
with_project: true
|
||||
},
|
||||
max_occurs: DashboardContent::MAX_MULTIPLE_OCCURS },
|
||||
'text' => { label: l(:label_text),
|
||||
'text' => { label: l(:label_text_sync),
|
||||
max_occurs: MAX_MULTIPLE_OCCURS,
|
||||
partial: 'dashboards/blocks/text' },
|
||||
'text_async' => { label: l(:label_text_async),
|
||||
max_occurs: MAX_MULTIPLE_OCCURS,
|
||||
async: { required_settings: %i[text],
|
||||
partial: 'dashboards/blocks/text_async' } },
|
||||
'news' => { label: l(:label_news_latest),
|
||||
permission: :view_news },
|
||||
'documents' => { label: l(:label_document_plural),
|
||||
|
|
|
@ -3,4 +3,7 @@ class DashboardRole < ActiveRecord::Base
|
|||
|
||||
belongs_to :dashboard
|
||||
belongs_to :role
|
||||
|
||||
validates :dashboard, :role,
|
||||
presence: true
|
||||
end
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
Deface::Override.new virtual_path: 'account/register',
|
||||
name: 'add-invisble-captcha',
|
||||
insert_top: 'div.box',
|
||||
original: 'a9c303821376a8d83cba32654629d71cc3926a1d',
|
||||
partial: 'account/invisible_captcha'
|
|
@ -1,8 +1,15 @@
|
|||
unless Redmine::Plugin.installed? 'redmine_servicedesk'
|
||||
# redmine_contacts does not provide hook
|
||||
Deface::Override.new virtual_path: 'contacts/_form',
|
||||
name: 'contacts-form-hook',
|
||||
insert_bottom: 'div#contact_data',
|
||||
original: 'df6cae24cfd26e5299c45c427fbbd4e5f23c313e',
|
||||
partial: 'hooks/view_contacts_form'
|
||||
if defined?(CONTACTS_VERSION_TYPE) && CONTACTS_VERSION_TYPE == 'PRO version'
|
||||
Deface::Override.new virtual_path: 'contacts/_form',
|
||||
name: 'contacts-pro-form-hook',
|
||||
insert_bottom: 'div#contact_data',
|
||||
original: 'df6cae24cfd26e5299c45c427fbbd4e5f23c313e',
|
||||
partial: 'hooks/view_contacts_form'
|
||||
else
|
||||
Deface::Override.new virtual_path: 'contacts/_form',
|
||||
name: 'contacts-form-hook',
|
||||
insert_bottom: 'div#contact_data',
|
||||
original: '217049684a0bcd7e404dc6b5b2348aae47ac8a72',
|
||||
partial: 'hooks/view_contacts_form'
|
||||
end
|
||||
end
|
||||
|
|
5
plugins/additionals/app/overrides/wiki/edit.rb
Normal file
5
plugins/additionals/app/overrides/wiki/edit.rb
Normal file
|
@ -0,0 +1,5 @@
|
|||
Deface::Override.new virtual_path: 'wiki/edit',
|
||||
name: 'wiki-edit-bottom',
|
||||
insert_before: 'fieldset',
|
||||
original: 'ededb6cfd5adfe8a9723d00ce0ee23575c7cc44c',
|
||||
partial: 'hooks/view_wiki_form_bottom'
|
5
plugins/additionals/app/overrides/wiki/show.rb
Normal file
5
plugins/additionals/app/overrides/wiki/show.rb
Normal file
|
@ -0,0 +1,5 @@
|
|||
Deface::Override.new virtual_path: 'wiki/show',
|
||||
name: 'wiki-show-bottom',
|
||||
insert_before: 'p.wiki-update-info',
|
||||
original: 'd9f52aa98f1cb335314570d3f5403690f1b29145',
|
||||
partial: 'hooks/view_wiki_show_bottom'
|
|
@ -1,2 +0,0 @@
|
|||
- if Additionals.setting?(:invisible_captcha)
|
||||
= f.invisible_captcha :url, autocomplete: 'off'
|
|
@ -1,7 +1,7 @@
|
|||
- footer = Additionals.setting(:global_footer)
|
||||
- footer = Additionals.setting :global_footer
|
||||
- if footer.present?
|
||||
.additionals-footer
|
||||
= textilizable(footer)
|
||||
= textilizable footer
|
||||
|
||||
- if @additionals_help_items.present?
|
||||
javascript:
|
||||
|
@ -9,7 +9,7 @@
|
|||
$('a.help').parent().append("<ul class=\"menu-children\">#{escape_javascript @additionals_help_items}</ul>");
|
||||
});
|
||||
|
||||
- if Additionals.setting?(:open_external_urls)
|
||||
- if Additionals.setting? :open_external_urls
|
||||
javascript:
|
||||
$(function() {
|
||||
$('a.external').attr({ 'target': '_blank',
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
- if Additionals.setting?(:add_go_to_top)
|
||||
- if Additionals.setting? :add_go_to_top
|
||||
a#gototop
|
||||
|
|
|
@ -7,8 +7,7 @@ table.list.issue-report.table-of-values
|
|||
tbody
|
||||
- options = { set_filter: 1 }
|
||||
- @chart[:filters].each do |line|
|
||||
- if line[:filter]
|
||||
- options.merge! line[:filter]
|
||||
- options.merge! line[:filter] if line[:filter]
|
||||
tr class="#{cycle 'odd', 'even'}"
|
||||
td.name class="#{line[:id].to_s == '0' ? 'summary' : ''}"
|
||||
- if line[:filter].nil?
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
= render(partial: 'additionals/live_search_ajax_call.js', layout: false, formats: [:js])
|
||||
- unless defined? classes
|
||||
- classes = 'title'
|
||||
- classes = 'title' unless defined? classes
|
||||
h2 class="#{classes}"
|
||||
= @query.new_record? ? l(title) : h(@query.name)
|
||||
span.additionals-live-search
|
||||
= text_field_tag(:search,
|
||||
= text_field_tag :search,
|
||||
q,
|
||||
autocomplete: 'off',
|
||||
class: 'live-search-field',
|
||||
placeholder: l(:label_query_name_search))
|
||||
placeholder: defined?(placeholder) ? placeholder : l(:label_query_name_search)
|
||||
|
||||
javascript:
|
||||
observeLiveSearchField('search',
|
||||
'query-result-list')
|
||||
|
|
|
@ -2,5 +2,4 @@
|
|||
= additionals_library_load :font_awesome
|
||||
= stylesheet_link_tag 'additionals', plugin: 'additionals'
|
||||
= javascript_include_tag 'additionals', plugin: 'additionals'
|
||||
- unless Redmine::Plugin.installed? 'redmine_hrm'
|
||||
- render_custom_top_menu_item
|
||||
- render_custom_top_menu_item unless Redmine::Plugin.installed? 'redmine_hrm'
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
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
|
||||
});
|
||||
});
|
||||
});
|
|
@ -21,7 +21,7 @@ javascript:
|
|||
placeholder: "#{options[:placeholder].presence}",
|
||||
allowClear: #{options[:allow_clear].present? && options[:allow_clear] ? 'true' : 'false'},
|
||||
minimumInputLength: 0,
|
||||
width: "#{options[:width].presence || '60%'}",
|
||||
width: "#{options[:width].presence || '90%'}",
|
||||
templateResult: #{options[:template_result].presence || 'formatNameWithIcon'},
|
||||
#{options[:template_selection].present? ? ('templateSelection: ' + options[:template_selection]) : nil}
|
||||
})
|
||||
|
|
|
@ -16,8 +16,8 @@ fieldset.box
|
|||
- 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}][]",
|
||||
= check_box_tag "settings[#{setting_name_totals}][]",
|
||||
s.name,
|
||||
value,
|
||||
id: nil)
|
||||
id: nil
|
||||
= s.caption
|
||||
|
|
|
@ -1,30 +0,0 @@
|
|||
- 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,
|
||||
project: @project,
|
||||
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,
|
||||
project: @project,
|
||||
tags_without_color: defined?(tags_without_color) ? tags_without_color : false)
|
|
@ -1,7 +1,3 @@
|
|||
' Need Help? :
|
||||
= link_to(l(:label_additionals_doc),
|
||||
'https://additionals.readthedocs.io/en/latest/',
|
||||
class: 'external',
|
||||
target: '_blank',
|
||||
rel: 'noopener')
|
||||
= link_to_external l(:label_additionals_doc), 'https://additionals.readthedocs.io/en/latest/'
|
||||
= render_tabs additionals_settings_tabs
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
p
|
||||
= additionals_settings_checkbox :invisible_captcha,
|
||||
disabled: (true unless Setting.self_registration?)
|
||||
em.info
|
||||
= t(:invisible_captcha_info_html)
|
|
@ -1,6 +1,6 @@
|
|||
h2 = l(:label_settings_macros) + " (#{@available_macros.count})"
|
||||
|
||||
.info = t(:label_top_macros_help_html)
|
||||
.info = t :label_top_macros_help_html
|
||||
br
|
||||
.box
|
||||
- @available_macros.each do |macro, options|
|
||||
|
|
|
@ -1,13 +1,6 @@
|
|||
table.list
|
||||
tr
|
||||
td.name
|
||||
= l :label_system_info
|
||||
| :
|
||||
td.name
|
||||
= system_info
|
||||
tr
|
||||
td.name
|
||||
= l :label_uptime
|
||||
| :
|
||||
td.name
|
||||
= system_uptime
|
||||
- AdditionalsInfo.system_infos.each_value do |system_info|
|
||||
tr
|
||||
td.name
|
||||
= "#{system_info[:label]}:"
|
||||
td.name = system_info[:value]
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
== @tags.collect { |tag| { 'id' => tag.name, 'text' => tag.name } }.to_json
|
|
@ -9,10 +9,7 @@
|
|||
|
||||
.splitcontent
|
||||
.splitcontentleft
|
||||
|
||||
- if @dashboard.new_record?
|
||||
= hidden_field_tag 'dashboard[dashboard_type]', @dashboard.dashboard_type
|
||||
|
||||
= hidden_field_tag 'dashboard[dashboard_type]', @dashboard.dashboard_type if @dashboard.new_record?
|
||||
- if @project && @allowed_projects.present? && @allowed_projects.count > 1
|
||||
p
|
||||
= f.select :project_id,
|
||||
|
|
|
@ -6,6 +6,6 @@
|
|||
- events_by_day = activity_dashboard_data settings, dashboard
|
||||
- title = Additionals.true?(settings[:me_only]) ? l(:label_my_activity) : l(:label_activity)
|
||||
h3 = link_to title, activity_path(user_id: User.current,
|
||||
from: events_by_day.keys.first)
|
||||
from: events_by_day.keys.first)
|
||||
|
||||
= render partial: 'activities/activities', locals: { events_by_day: events_by_day }
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
- max_entries = settings[:max_entries].presence || DashboardContent::DEFAULT_MAX_ENTRIES
|
||||
div id="#{block}-settings" style="#{'display: none;' if hide}"
|
||||
= form_tag(_update_layout_setting_dashboard_path(@project, @dashboard), remote: true) do
|
||||
= hidden_field_tag "settings[#{block}][me_only]", '0'
|
||||
.box
|
||||
p
|
||||
label
|
||||
= l(:label_max_entries)
|
||||
= l :label_max_entries
|
||||
' :
|
||||
= number_field_tag "settings[#{block}][max_entries]", max_entries, min: 1, max: 1000, required: true
|
||||
|
||||
= number_field_tag "settings[#{block}][max_entries]",
|
||||
settings[:max_entries].presence || DashboardContent::DEFAULT_MAX_ENTRIES,
|
||||
min: 1, max: 1000, required: true
|
||||
p
|
||||
label
|
||||
= l(:label_only_my_activities)
|
||||
= l :label_only_my_activities
|
||||
' :
|
||||
= check_box_tag "settings[#{block}][me_only]", '1', Additionals.true?(settings[:me_only])
|
||||
|
||||
|
|
|
@ -11,10 +11,7 @@
|
|||
- if feed[:items].count.positive?
|
||||
ul.reporting-list.feed
|
||||
- feed[:items].each do |item|
|
||||
li
|
||||
= link_to item[:title],
|
||||
item[:link],
|
||||
class: 'external', rel: 'noopener noreferrer', target: '_blank'
|
||||
li = link_to_external item[:title], item[:link]
|
||||
- else
|
||||
p.nodata = l :label_no_data
|
||||
- elsif settings[:url].blank?
|
||||
|
|
|
@ -6,7 +6,7 @@ h3.icon.icon-news = l :label_news_latest
|
|||
.box
|
||||
p
|
||||
label
|
||||
= l(:label_max_entries)
|
||||
= l :label_max_entries
|
||||
' :
|
||||
= number_field_tag "settings[#{block}][max_entries]", max_entries, min: 1, max: 1000, required: true
|
||||
p
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
- if @subprojects.any?
|
||||
h3.icon.icon-projects
|
||||
= l(:label_subproject_plural)
|
||||
= l :label_subproject_plural
|
||||
ul.subprojects
|
||||
- @subprojects.each do |project|
|
||||
li
|
||||
= link_to(project.name, project_path(project), class: project.css_classes).html_safe
|
||||
= link_to project.name, project_path(project), class: project.css_classes
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
- if count.positive?
|
||||
/ required by some helpers of other plugins
|
||||
- @query = query
|
||||
|
||||
= render partial: query_block[:list_partial],
|
||||
locals: { query_block[:entities_var] => query.send(query_block[:entries_method],
|
||||
limit: settings[:max_entries] || DashboardContent::DEFAULT_MAX_ENTRIES),
|
||||
|
|
|
@ -1,26 +1,12 @@
|
|||
ruby:
|
||||
title = settings[:title] || l(:label_text)
|
||||
text = settings[:text]
|
||||
|
||||
- if title.present?
|
||||
h3 = title
|
||||
- if settings[:text].nil?
|
||||
h3
|
||||
= l :label_text_sync
|
||||
- elsif settings[:title].present?
|
||||
h3
|
||||
= settings[:title]
|
||||
|
||||
- if @can_edit
|
||||
div id="#{block}-settings" style='display: none;'
|
||||
= form_tag(_update_layout_setting_dashboard_path(@project, @dashboard), remote: true) do
|
||||
.box
|
||||
p
|
||||
label
|
||||
= l :field_title
|
||||
' :
|
||||
= text_field_tag "settings[#{block}][title]", title
|
||||
p
|
||||
= text_area_tag "settings[#{block}][text]", text, rows: addtionals_textarea_cols(text), class: 'wiki-edit'
|
||||
= wikitoolbar_for "settings_#{block}_text"
|
||||
p
|
||||
= submit_tag l(:button_save)
|
||||
'
|
||||
= link_to_function l(:button_cancel), "$('##{block}-settings').toggle();"
|
||||
= render partial: 'dashboards/blocks/text_async_settings', locals: { block: block, settings: settings }
|
||||
|
||||
.wiki
|
||||
= textilizable text
|
||||
= textilizable settings[:text]
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
- cache render_async_cache_key(_dashboard_async_blocks_path(@project,
|
||||
dashboard.async_params(block, async, settings))),
|
||||
expires_in: async[:cache_expires_in] || DashboardContent::RENDER_ASYNC_CACHE_EXPIRES_IN,
|
||||
skip_digest: true do
|
||||
|
||||
- if settings[:title].present?
|
||||
h3 = settings[:title]
|
||||
|
||||
.wiki
|
||||
= textilizable settings[:text]
|
|
@ -0,0 +1,15 @@
|
|||
div id="#{block}-settings" style='display: none;'
|
||||
= form_tag(_update_layout_setting_dashboard_path(@project, @dashboard), remote: true) do
|
||||
.box
|
||||
p
|
||||
label
|
||||
= l :field_title
|
||||
' :
|
||||
= text_field_tag "settings[#{block}][title]", (settings[:title] || l(:label_text_sync))
|
||||
p
|
||||
= text_area_tag "settings[#{block}][text]", settings[:text], rows: addtionals_textarea_cols(settings[:text]), class: 'wiki-edit'
|
||||
= wikitoolbar_for "settings_#{block}_text"
|
||||
p
|
||||
= submit_tag l :button_save
|
||||
'
|
||||
= link_to_function l(:button_cancel), "$('##{block}-settings').toggle();"
|
|
@ -1,7 +1,6 @@
|
|||
h2 = l(:button_dashboard_edit)
|
||||
= labelled_form_for :dashboard,
|
||||
@dashboard,
|
||||
url: { action: 'update', id: @dashboard.id },
|
||||
html: { multipart: true, method: :put, id: 'dashboard-form' } do |f|
|
||||
html: { multipart: true, id: 'dashboard-form' } do |f|
|
||||
= render partial: 'form', locals: { f: f }
|
||||
= submit_tag l(:button_save)
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
= call_hook :view_wiki_form_bottom, content: @content, page: @page
|
|
@ -0,0 +1 @@
|
|||
= call_hook :view_wiki_show_bottom, content: @content, page: @page
|
|
@ -1,5 +1,9 @@
|
|||
- 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 }
|
||||
- if User.current.logged? && \
|
||||
Additionals.setting?(:issue_assign_to_me) && \
|
||||
@issue.editable? && \
|
||||
@issue.safe_attribute?('assigned_to_id') && \
|
||||
@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'
|
||||
|
|
|
@ -1,19 +1,19 @@
|
|||
- 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)
|
||||
@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)
|
||||
h3 = l :label_issue_change_status
|
||||
ul.issue-status-change-sidebar
|
||||
- statuses.each do |s|
|
||||
- if s != @issue.status
|
||||
- unless s == @issue.status
|
||||
li
|
||||
- if s.is_closed?
|
||||
= link_to(font_awesome_icon('fas_caret-square-left', post_text: s.name),
|
||||
= 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}")
|
||||
method: :put, class: "status-switch status-#{s.id}"
|
||||
- else
|
||||
= link_to(font_awesome_icon('far_caret-square-left', post_text: s.name),
|
||||
= 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}")
|
||||
method: :put, class: "status-switch status-#{s.id}"
|
||||
|
|
|
@ -6,8 +6,7 @@
|
|||
edit_project_dashboard_path(@project, @dashboard),
|
||||
class: 'icon icon-edit'
|
||||
|
||||
- unless Redmine::Plugin.installed? 'redmine_reporting'
|
||||
= bookmark_link @project
|
||||
= bookmark_link @project unless Redmine::Plugin.installed? 'redmine_reporting'
|
||||
= call_hook :view_project_contextual_links, project: @project
|
||||
|
||||
- if @dashboard&.editable?
|
||||
|
@ -49,9 +48,7 @@
|
|||
class: 'icon icon-del'
|
||||
|
||||
= sidebar_action_toggle @dashboard_sidebar, @dashboard, @project
|
||||
- unless @dashboard_sidebar
|
||||
= render_dashboard_actionlist @dashboard, @project
|
||||
|
||||
= render_dashboard_actionlist @dashboard, @project unless @dashboard_sidebar
|
||||
= call_hook :view_project_actions_dropdown, project: @project
|
||||
|
||||
- if User.current.allowed_to?(:edit_project, @project)
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
- 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')
|
||||
- project_url = memberships_new_issue_project_url user, @memberships
|
||||
= link_to l(:label_issue_new), project_url, class: 'user-new-issue icon icon-add' if project_url.present?
|
||||
|
|
|
@ -21,8 +21,7 @@
|
|||
= delete_dashboard_link dashboard_path(@dashboard),
|
||||
class: 'icon icon-del'
|
||||
= sidebar_action_toggle @dashboard_sidebar, @dashboard
|
||||
- unless @dashboard_sidebar
|
||||
= render_dashboard_actionlist @dashboard
|
||||
= render_dashboard_actionlist @dashboard unless @dashboard_sidebar
|
||||
|
||||
= call_hook :view_welcome_show_actions_dropdown
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
= 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
|
||||
|
@ -16,6 +17,7 @@
|
|||
= l :field_login
|
||||
' :
|
||||
= link_to user.login, "/users/#{user.id}"
|
||||
|
||||
- unless user.pref.hide_mail
|
||||
.user.line
|
||||
= l :field_mail
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue