Working on ruby 2.7 updates and warning fixes

This commit is contained in:
Alexander Meindl 2021-04-18 13:34:55 +02:00
parent a48ee5b4a7
commit 07c306fa66
172 changed files with 1035 additions and 751 deletions

View File

@ -14,7 +14,8 @@ jobs:
- name: Setup Gemfile
run: |
cp test/support/gemfile.rb Gemfile
touch .enable_dev
sed -i "3isource 'https://rubygems.org'" Gemfile
- name: Setup Ruby
uses: ruby/setup-ruby@v1

2
.gitignore vendored
View File

@ -6,7 +6,9 @@ tmp/
Gemfile.lock
.project
.vscode
.bundle
.settings/
docs/_build
docs/_static
docs/_templates
.enable_dev

View File

@ -16,6 +16,10 @@ Metrics/AbcSize:
Metrics/BlockLength:
Enabled: false
Metrics/ParameterLists:
Enabled: true
CountKeywordArgs: false
Metrics/ClassLength:
Enabled: false
@ -58,16 +62,45 @@ Performance/ChainArrayAllocation:
Style/AutoResourceCleanup:
Enabled: true
Style/ExpandPathArguments:
Enabled: true
Exclude:
- additionals.gemspec
- test/**/*
Style/FrozenStringLiteralComment:
Enabled: false
Enabled: true
Exclude:
- '/**/*.rsb'
Style/OptionHash:
Enabled: true
SuspiciousParamNames:
- options
- api_options
- opts
- args
- params
- parameters
- settings
Exclude:
- lib/additionals/patches/*.rb
Style/ReturnNil:
Enabled: true
Style/UnlessLogicalOperators:
Enabled: true
Style/MethodCallWithArgsParentheses:
Enabled: true
AllowParenthesesInMultilineCall: true
AllowParenthesesInChaining: true
EnforcedStyle: omit_parentheses
Style/Documentation:
Enabled: false
# required for travis/ci (symbolic links problem)
Style/ExpandPathArguments:
Enabled: false
Style/HashTransformValues:
Enabled: false

View File

@ -4,6 +4,7 @@ linters:
RuboCop:
ignored_cops:
- Layout/ArgumentAlignment
- Layout/ArrayAlignment
- Layout/BlockEndNewline
- Layout/EmptyLineAfterGuardClause
- Layout/HashAlignment
@ -21,6 +22,7 @@ linters:
- Lint/Void
- Metrics/BlockNesting
- Rails/OutputSafety
- Style/FrozenStringLiteralComment
- Style/IdenticalConditionalBranches
- Style/IfUnlessModifier
- Style/Next

View File

@ -1,6 +1,11 @@
Changelog
=========
3.0.3
+++++
- Ruby 2.7 warnings fixed
3.0.2
+++++

18
Gemfile
View File

@ -1,2 +1,20 @@
# frozen_string_literal: true
# Specify your gem's dependencies in additionals.gemspec
gemspec
group :development do
# this is only used for development.
# if you want to use it, do:
# - create .enable_dev file in additionals directory
# - remove rubocop entries from REDMINE/Gemfile
# - remove REDMINE/.rubocop* files
if File.file? File.expand_path './.enable_dev', __dir__
gem 'brakeman', require: false
gem 'pandoc-ruby', require: false
gem 'rubocop', require: false
gem 'rubocop-performance', require: false
gem 'rubocop-rails', require: false
gem 'slim_lint', require: false
end
end

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
require 'bundler/gem_tasks'
require 'rake/testtask'

View File

@ -1,5 +1,7 @@
# frozen_string_literal: true
lib = File.expand_path '../lib', __FILE__
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
$LOAD_PATH.unshift lib unless $LOAD_PATH.include? lib
require 'additionals/version'
Gem::Specification.new do |spec|
@ -22,9 +24,4 @@ Gem::Specification.new do |spec|
spec.add_runtime_dependency 'render_async'
spec.add_runtime_dependency 'rss'
spec.add_runtime_dependency 'slim-rails'
spec.add_development_dependency 'brakeman'
spec.add_development_dependency 'bundler'
spec.add_development_dependency 'rake'
spec.add_development_dependency 'slim_lint'
end

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
class AdditionalsAssignToMeController < ApplicationController
before_action :find_issue
helper :additionals_issues
@ -11,11 +13,11 @@ class AdditionalsAssignToMeController < ApplicationController
return
end
@issue.init_journal(User.current)
@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)
flash[:error] = l :error_issues_could_not_be_assigned_to_me
return redirect_to(issue_path(@issue))
end
@ -29,7 +31,7 @@ class AdditionalsAssignToMeController < ApplicationController
private
def find_issue
@issue = Issue.find(params[:issue_id])
@issue = Issue.find params[:issue_id]
raise Unauthorized unless @issue.visible? && @issue.editable?
@project = @issue.project

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
class AdditionalsChangeStatusController < ApplicationController
before_action :find_issue
helper :additionals_issues
@ -6,7 +8,7 @@ class AdditionalsChangeStatusController < ApplicationController
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)
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))
@ -18,7 +20,7 @@ class AdditionalsChangeStatusController < ApplicationController
@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)
flash[:error] = l :error_issue_status_could_not_changed
return redirect_to(issue_path(@issue))
end
@ -33,7 +35,7 @@ class AdditionalsChangeStatusController < ApplicationController
private
def find_issue
@issue = Issue.find(params[:issue_id])
@issue = Issue.find params[:issue_id]
raise Unauthorized unless @issue.visible? && @issue.editable?
@project = @issue.project

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
class AdditionalsMacrosController < ApplicationController
before_action :require_login

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
require 'open-uri'
class DashboardAsyncBlocksController < ApplicationController
@ -84,7 +86,7 @@ class DashboardAsyncBlocksController < ApplicationController
rescue ActiveRecord::RecordNotFound
render_404
end
deny_access unless User.current.allowed_to?(:view_project, @project)
deny_access unless User.current.allowed_to? :view_project, @project
@project
end

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
class DashboardsController < ApplicationController
menu_item :dashboards
@ -63,7 +65,7 @@ class DashboardsController < ApplicationController
end
def create
@dashboard = Dashboard.new(author: User.current)
@dashboard = Dashboard.new author: User.current
@dashboard.safe_attributes = params[:dashboard]
@dashboard.dashboard_type = assign_dashboard_type
@dashboard.role_ids = params[:dashboard][:role_ids] if params[:dashboard].present?
@ -71,7 +73,7 @@ class DashboardsController < ApplicationController
@allowed_projects = @dashboard.allowed_target_projects
if @dashboard.save
flash[:notice] = l(:notice_successful_create)
flash[:notice] = l :notice_successful_create
respond_to do |format|
format.html { redirect_to dashboard_link_path(@project, @dashboard) }
@ -80,13 +82,13 @@ class DashboardsController < ApplicationController
else
respond_to do |format|
format.html { render action: 'new' }
format.api { render_validation_errors(@dashboard) }
format.api { render_validation_errors @dashboard }
end
end
end
def edit
return render_403 unless @dashboard.editable_by?(User.current)
return render_403 unless @dashboard.editable_by? User.current
@allowed_projects = @dashboard.allowed_target_projects
@ -97,7 +99,7 @@ class DashboardsController < ApplicationController
end
def update
return render_403 unless @dashboard.editable_by?(User.current)
return render_403 unless @dashboard.editable_by? User.current
@dashboard.safe_attributes = params[:dashboard]
@dashboard.role_ids = params[:dashboard][:role_ids] if params[:dashboard].present?
@ -106,9 +108,9 @@ class DashboardsController < ApplicationController
@allowed_projects = @dashboard.allowed_target_projects
if @dashboard.save
flash[:notice] = l(:notice_successful_update)
flash[:notice] = l :notice_successful_update
respond_to do |format|
format.html { redirect_to dashboard_link_path @project, @dashboard }
format.html { redirect_to dashboard_link_path(@project, @dashboard) }
format.api { head :ok }
end
else
@ -124,20 +126,20 @@ class DashboardsController < ApplicationController
begin
@dashboard.destroy
flash[:notice] = l(:notice_successful_delete)
flash[:notice] = l :notice_successful_delete
respond_to do |format|
format.html { redirect_to @project.nil? ? home_path : project_path(@project) }
format.api { head :ok }
end
rescue ActiveRecord::RecordNotDestroyed
flash[:error] = l(:error_remove_db_entry)
flash[:error] = l :error_remove_db_entry
redirect_to dashboard_path(@dashboard)
end
end
def query_statement_invalid(exception)
logger&.error "Query::StatementInvalid: #{exception.message}"
session.delete(additionals_query_session_key('dashboard'))
session.delete additionals_query_session_key('dashboard')
render_error l(:error_query_statement_invalid)
end
@ -145,7 +147,7 @@ class DashboardsController < ApplicationController
block_settings = params[:settings] || {}
block_settings.each do |block, settings|
@dashboard.update_block_settings(block, settings.to_unsafe_hash)
@dashboard.update_block_settings block, settings.to_unsafe_hash
end
@dashboard.save
@updated_blocks = block_settings.keys
@ -201,7 +203,7 @@ class DashboardsController < ApplicationController
end
def find_dashboard
@dashboard = Dashboard.find(params[:id])
@dashboard = Dashboard.find params[:id]
raise ::Unauthorized unless @dashboard.visible?
if @dashboard.dashboard_type == DashboardContentProject::TYPE_NAME && @dashboard.project.nil?

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
module AdditionalsChartjsHelper
def chartjs_colorschemes_info_url
link_to(l(:label_chartjs_colorscheme_info),
@ -7,6 +9,6 @@ module AdditionalsChartjsHelper
def select_options_for_chartjs_colorscheme(selected)
data = YAML.safe_load(ERB.new(IO.read(File.join(Additionals.plugin_dir, 'config', 'colorschemes.yml'))).result) || {}
grouped_options_for_select(data, selected)
grouped_options_for_select data, selected
end
end

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
module AdditionalsClipboardjsHelper
def clipboardjs_button_for(target, clipboard_text_from_button = nil)
render_clipboardjs_button(target, clipboard_text_from_button) + render_clipboardjs_javascript(target)
@ -34,6 +36,6 @@ module AdditionalsClipboardjsHelper
end
def render_clipboardjs_javascript(target)
javascript_tag("setClipboardJS('#zc_#{target}');")
javascript_tag "setClipboardJS('#zc_#{target}');"
end
end

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
module AdditionalsCustomFieldsHelper
def custom_fields_with_full_with_layout
['IssueCustomField']

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
module AdditionalsFontawesomeHelper
# name = TYPE-FA_NAME, eg. fas_car
# fas_cloud-upload-alt
@ -7,7 +9,7 @@ module AdditionalsFontawesomeHelper
# pre_text
# post_text
# title
def font_awesome_icon(name, options = {})
def font_awesome_icon(name, **options)
info = AdditionalsFontAwesome.value_info name
return '' if info.blank?
@ -29,7 +31,7 @@ module AdditionalsFontawesomeHelper
post_text = options[:post_text]
options.delete :post_text
end
s << tag.span(options)
s << tag.span(**options)
if post_text.present?
s << ' '
s << post_text
@ -37,16 +39,16 @@ module AdditionalsFontawesomeHelper
safe_join s
end
def additionals_fontawesome_select(form, selected, options = {})
def additionals_fontawesome_select(form, selected, **options)
options[:include_blank] ||= true unless options[:required]
html_options = {}
additionals_fontawesome_add_selected selected
name, options = Additionals.hash_remove_with_default(:name, options, :icon)
loader, options = Additionals.hash_remove_with_default(:loader, options, true)
html_options[:class], options = Additionals.hash_remove_with_default(:class, options, 'select2-fontawesome-field')
html_options[:style], options = Additionals.hash_remove_with_default(:style, options)
name, options = Additionals.hash_remove_with_default :name, options, :icon
loader, options = Additionals.hash_remove_with_default :loader, options, true
html_options[:class], options = Additionals.hash_remove_with_default :class, options, 'select2-fontawesome-field'
html_options[:style], options = Additionals.hash_remove_with_default :style, options
s = []
s << form.select(name,
@ -54,7 +56,7 @@ module AdditionalsFontawesomeHelper
options,
html_options)
s << additionals_fontawesome_loader(options, html_options) if loader
s << additionals_fontawesome_loader(**options, field_class: html_options[:class]) if loader
safe_join s
end
@ -70,21 +72,20 @@ module AdditionalsFontawesomeHelper
'250px'
end
def additionals_fontawesome_loader(options, html_options = {})
html_options[:class] ||= 'select2-fontawesome-field'
def additionals_fontawesome_loader(field_class: 'select2-fontawesome-field', **options)
options[:template_selection] = 'formatFontawesomeText'
options[:template_result] = 'formatFontawesomeText'
if options[:include_blank]
options[:placeholder] ||= l(:label_disabled)
options[:placeholder] ||= l :label_disabled
options[:allow_clear] ||= true
end
options[:width] = additionals_fontawesome_default_select_width
render(layout: false,
partial: 'additionals/select2_ajax_call.js',
render layout: false,
partial: 'additionals/select2_ajax_call',
formats: [:js],
locals: { field_class: html_options[:class],
locals: { field_class: field_class,
ajax_url: fontawesome_auto_completes_path(selected: @selected_store.join(',')),
options: options })
options: options }
end
end

View File

@ -1,7 +1,9 @@
# frozen_string_literal: true
module AdditionalsIssuesHelper
def author_options_for_select(project, entity = nil, permission = nil)
scope = project.present? ? project.users.visible : User.active.visible
scope = scope.with_permission(permission, project) unless permission.nil?
scope = scope.with_permission permission, project unless permission.nil?
authors = scope.sorted.to_a
unless entity.nil?
@ -15,7 +17,7 @@ module AdditionalsIssuesHelper
s = []
return s unless authors.any?
s << tag.option("<< #{l :label_me} >>", value: User.current.id) if authors.include?(User.current)
s << tag.option("<< #{l :label_me} >>", value: User.current.id) if authors.include? User.current
if entity.nil?
s << options_from_collection_for_select(authors, 'id', 'name')

View File

@ -1,13 +1,15 @@
# frozen_string_literal: true
module AdditionalsJournalsHelper
MultipleValuesDetail = Struct.new(:property, :prop_key, :custom_field, :old_value, :value)
MultipleValuesDetail = Struct.new :property, :prop_key, :custom_field, :old_value, :value
# Returns the textual representation of a journal details
# as an array of strings
def entity_details_to_strings(entity, details, options = {})
def entity_details_to_strings(entity, details, **options)
entity_type = entity.model_name.param_key
show_detail_method = "#{entity_type}_show_detail"
options[:only_path] = options[:only_path] != false
no_html = options.delete(:no_html)
no_html = options.delete :no_html
strings = []
values_by_field = {}
@ -21,21 +23,21 @@ module AdditionalsJournalsHelper
next
end
end
strings << send(show_detail_method, detail, no_html, options)
strings << send(show_detail_method, detail, no_html, **options)
end
if values_by_field.present?
values_by_field.each do |field, changes|
if changes[:added].any?
detail = MultipleValuesDetail.new('cf', field.id.to_s, field)
detail = MultipleValuesDetail.new 'cf', field.id.to_s, field
detail.value = changes[:added]
strings << send(show_detail_method, detail, no_html, options)
strings << send(show_detail_method, detail, no_html, **options)
end
next unless changes[:deleted].any?
detail = MultipleValuesDetail.new('cf', field.id.to_s, field)
detail = MultipleValuesDetail.new 'cf', field.id.to_s, field
detail.old_value = changes[:deleted]
strings << send(show_detail_method, detail, no_html, options)
strings << send(show_detail_method, detail, no_html, **options)
end
end
strings
@ -64,7 +66,7 @@ module AdditionalsJournalsHelper
# Returns the textual representation of a single journal detail
# rubocop: disable Rails/OutputSafety
def entity_show_detail(entity, detail, no_html = false, options = {}) # rubocop:disable Style/OptionalBooleanParameter:
def entity_show_detail(entity, detail, no_html = false, **options) # rubocop:disable Style/OptionalBooleanParameter:
multiple = false
no_detail = false
show_diff = false
@ -82,17 +84,17 @@ module AdditionalsJournalsHelper
if label || show_diff
unless no_html
label = tag.strong(label)
old_value = tag.i(old_value) if detail.old_value
old_value = tag.del(old_value) if detail.old_value && detail.value.blank?
value = tag.i(value) if value
label = tag.strong label
old_value = tag.i old_value if detail.old_value
old_value = tag.del old_value if detail.old_value && detail.value.blank?
value = tag.i value if value
end
html =
if no_detail
l(:text_journal_changed_no_detail, label: label)
l :text_journal_changed_no_detail, label: label
elsif show_diff
s = l(:text_journal_changed_no_detail, label: label)
s = l :text_journal_changed_no_detail, label: label
unless no_html
diff_link = link_to l(:label_diff),
send(diff_url_method,
@ -105,11 +107,11 @@ module AdditionalsJournalsHelper
s.html_safe
elsif detail.value.present?
if detail.old_value.present?
l(:text_journal_changed, label: label, old: old_value, new: value)
l :text_journal_changed, label: label, old: old_value, new: value
elsif multiple
l(:text_journal_added, label: label, value: value)
l :text_journal_added, label: label, value: value
else
l(:text_journal_set_to, label: label, value: value)
l :text_journal_set_to, label: label, value: value
end
else
l(:text_journal_deleted, label: label, old: old_value).html_safe
@ -138,20 +140,20 @@ module AdditionalsJournalsHelper
prop = { label: custom_field.name }
project = Project.visible.where(id: detail.value).first if detail.value.present?
old_project = Project.visible.where(id: detail.old_value).first if detail.old_value.present?
prop[:value] = link_to_project(project) if project.present?
prop[:old_value] = link_to_project(old_project) if old_project.present?
prop[:value] = link_to_project project if project.present?
prop[:old_value] = link_to_project old_project if old_project.present?
when 'db_entry'
prop = { label: custom_field.name }
db_entry = DbEntry.visible.where(id: detail.value).first if detail.value.present?
old_db_entry = DbEntry.visible.where(id: detail.old_value).first if detail.old_value.present?
prop[:value] = link_to(db_entry.name, db_entry_url(db_entry)) if db_entry.present?
prop[:old_value] = link_to(old_db_entry.name, db_entry_url(old_db_entry)) if old_db_entry.present?
prop[:value] = link_to db_entry.name, db_entry_url(db_entry) if db_entry.present?
prop[:old_value] = link_to old_db_entry.name, db_entry_url(old_db_entry) if old_db_entry.present?
when 'password'
prop = { label: custom_field.name }
password = Password.visible.where(id: detail.value).first if detail.value.present? && defined?(Password)
old_password = Password.visible.where(id: detail.old_value).first if detail.old_value.present? && defined?(Password)
prop[:value] = link_to(password.name, password_url(password)) if password.present?
prop[:old_value] = link_to(old_password.name, password_url(old_password)) if old_password.present?
prop[:value] = link_to password.name, password_url(password) if password.present?
prop[:old_value] = link_to old_password.name, password_url(old_password) if old_password.present?
end
prop

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
module AdditionalsMenuHelper
def additionals_top_menu_setup
return if Redmine::Plugin.installed? 'redmine_hrm'
@ -18,11 +20,11 @@ module AdditionalsMenuHelper
end
end
def handle_top_submenu_item(menu_name, item)
handle_top_menu_item menu_name, item, with_submenu: true
def handle_top_submenu_item(menu_name, **item)
handle_top_menu_item menu_name, with_submenu: true, **item
end
def handle_top_menu_item(menu_name, item, with_submenu: false)
def handle_top_menu_item(menu_name, with_submenu: false, **item)
Redmine::MenuManager.map(:top_menu).delete(menu_name.to_sym) if Redmine::MenuManager.map(:top_menu).exists?(menu_name.to_sym)
html_options = {}
@ -30,7 +32,7 @@ module AdditionalsMenuHelper
css_classes = []
css_classes << 'top-submenu' if with_submenu
css_classes << 'external' if item[:url].include? '://'
html_options[:class] = css_classes.join(' ') if css_classes.present?
html_options[:class] = css_classes.join ' ' if css_classes.present?
html_options[:title] = item[:title] if item[:title].present?
@ -40,9 +42,9 @@ module AdditionalsMenuHelper
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])
font_awesome_icon item[:symbol], post_text: item[:name]
elsif item[:symbol].present?
font_awesome_icon(item[:symbol])
font_awesome_icon item[:symbol]
elsif item[:name].present?
item[:name].to_s
end
@ -149,8 +151,8 @@ module AdditionalsMenuHelper
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?
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?
@ -186,12 +188,12 @@ module AdditionalsMenuHelper
s << if item[:title] == '-'
tag.li tag.hr
else
html_options = { class: "help_item_#{idx}" }
html_options = { class: +"help_item_#{idx}" }
if item[:url].include? '://'
html_options[:class] << ' external'
html_options[:target] = '_blank'
end
tag.li(link_to(item[:title], item[:url], html_options))
tag.li link_to(item[:title], item[:url], html_options)
end
end
safe_join s
@ -200,13 +202,8 @@ module AdditionalsMenuHelper
# 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 }],
plugins = { redmine_drawio: [{ title: 'draw.io usage',
url: 'https://github.com/mikitex70/redmine_drawio#usage' }],
redmine_contacts: [{ title: 'Redmine CRM',
url: 'https://www.redmineup.com/pages/help/crm',
admin: true }],

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
module AdditionalsProjectsHelper
def project_overview_name(_project, dashboard = nil)
name = [l(:label_overview)]

View File

@ -1,22 +1,28 @@
# frozen_string_literal: true
module AdditionalsQueriesHelper
def additionals_query_session_key(object_type)
"#{object_type}_query".to_sym
end
def additionals_retrieve_query(object_type, options = {})
def additionals_retrieve_query(object_type, user_filter: nil)
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)
additionals_load_query_id query_class,
session_key,
params[:query_id],
object_type,
user_filter: user_filter
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 = query_class.new name: '_'
@query.project = @project
@query.user_filter = options[:user_filter] if options[:user_filter]
@query.build_from_params(params)
@query.user_filter = user_filter if 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),
@ -27,7 +33,7 @@ module AdditionalsQueriesHelper
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]
@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],
@ -51,14 +57,14 @@ module AdditionalsQueriesHelper
end
end
def additionals_load_query_id(query_class, session_key, query_id, options, object_type)
scope = query_class.where(project_id: nil)
scope = scope.or(query_class.where(project_id: @project.id)) if @project
@query = scope.find(query_id)
def additionals_load_query_id(query_class, session_key, query_id, object_type, user_filter: nil)
scope = query_class.where project_id: nil
scope = scope.or query_class.where(project_id: @project.id) if @project
@query = scope.find query_id
raise ::Unauthorized unless @query.visible?
@query.project = @project
@query.user_filter = options[:user_filter] if options[:user_filter]
@query.user_filter = user_filter if user_filter
session[session_key] = { id: @query.id, project_id: @query.project_id }
@query.sort_criteria = params[:sort] if params[:sort].present?
@ -76,62 +82,58 @@ module AdditionalsQueriesHelper
"#{object_type}_query_data_#{session.id}_#{project_id}"
end
def additionals_select2_search_users(options = {})
def additionals_select2_search_users(all_visible: false, where_filter: nil, where_params: nil)
q = params[:q].to_s.strip
exclude_id = params[:user_id].to_i
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?
scope = scope.visible unless all_visible
scope = scope.where.not id: exclude_id if exclude_id.positive?
scope = scope.where where_filter, where_params if where_filter
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 }
render layout: false, partial: 'auto_completes/additionals_users'
end
def additionals_query_to_xlsx(items, query, options = {})
def additionals_query_to_xlsx(items, query, no_id_link: false)
require 'write_xlsx'
columns = if options[:columns].present? || options[:c].present?
query.available_columns
else
query.columns
end
options[:filename] = StringIO.new('')
options = { no_id_link: no_id_link,
filename: StringIO.new(+'') }
export_to_xlsx(items, columns, options)
export_to_xlsx items, query.columns, options
options[:filename].string
end
def additionals_result_to_xlsx(items, columns, options = {})
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)
export_to_xlsx items, columns, options
end
def export_to_xlsx(items, columns, options)
workbook = WriteXLSX.new(options[:filename])
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)
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)
send options[:xlsx_write_header_row], workbook, worksheet, columns
else
xlsx_write_header_row(workbook, worksheet, columns)
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)
send options[:xlsx_write_item_rows], workbook, worksheet, columns, items, options
else
xlsx_write_item_rows(workbook, worksheet, columns, items, options)
xlsx_write_item_rows workbook, worksheet, columns, items, options
end
columns.size.times do |index|
worksheet.set_column(index, index, options[:columns_width][index])
worksheet.set_column index, index, options[:columns_width][index]
end
workbook.close
@ -152,20 +154,20 @@ module AdditionalsQueriesHelper
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)
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)
value = csv_content(c, line).dup
if c.name == :id # ID
if options[:no_id_link].blank?
link = url_for(controller: line.class.name.underscore.pluralize, action: 'show', id: line.id)
worksheet.write(line_index + 1, column_index, link, hyperlink_format, value)
link = url_for controller: line.class.name.underscore.pluralize, action: 'show', id: line.id
worksheet.write line_index + 1, column_index, link, hyperlink_format, value
else
# id without link
worksheet.write(line_index + 1,
@ -173,13 +175,13 @@ module AdditionalsQueriesHelper
value,
workbook.add_format(xlsx_cell_format(:cell, value, line_index)))
end
elsif xlsx_hyperlink_cell?(value)
worksheet.write(line_index + 1, column_index, value[0..254], hyperlink_format, value)
elsif xlsx_hyperlink_cell? value
worksheet.write line_index + 1, column_index, value[0..254], hyperlink_format, value
elsif !c.inline?
# block column can be multiline strings
value.gsub!("\r\n", "\n")
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)
worksheet.write_rich_string line_index + 1, column_index, value, text_format
else
worksheet.write(line_index + 1,
column_index,
@ -187,7 +189,7 @@ module AdditionalsQueriesHelper
workbook.add_format(xlsx_cell_format(:cell, value, line_index)))
end
width = xlsx_get_column_width(value)
width = xlsx_get_column_width value
options[:columns_width][column_index] = width if options[:columns_width][column_index] < width
end
end
@ -238,7 +240,7 @@ module AdditionalsQueriesHelper
# 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)
tags = hidden_field_tag('set_filter', '1', id: nil)
tags = hidden_field_tag 'set_filter', '1', id: nil
if query.filters.present?
query.filters.each do |field, filter|
@ -254,7 +256,7 @@ module AdditionalsQueriesHelper
ignored_block_columns = query.block_columns.map(&:name)
query.columns.each do |column|
next if ignored_block_columns.include?(column.name)
next if ignored_block_columns.include? column.name
tags << hidden_field_tag('c[]', column.name, id: nil)
end

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
module AdditionalsRoutesHelper
def _dashboards_path(project, *args)
if project
@ -71,7 +73,7 @@ module AdditionalsRoutesHelper
end
end
def dashboard_link_path(project, dashboard, options = {})
def dashboard_link_path(project, dashboard, **options)
options[:dashboard_id] = dashboard.id
if dashboard.dashboard_type == DashboardContentProject::TYPE_NAME
project_path project, options

View File

@ -1,6 +1,8 @@
# frozen_string_literal: true
module AdditionalsSelect2Helper
def additionals_select2_tag(name, option_tags = nil, options = {})
s = select_tag(name, option_tags, options)
def additionals_select2_tag(name, option_tags, 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)
@ -8,7 +10,7 @@ module AdditionalsSelect2Helper
end
# Transforms select filters of +type+ fields into select2
def additionals_transform_to_select2(type, options = {})
javascript_tag("setSelect2Filter('#{type}', #{options.to_json});") unless type.empty?
def additionals_transform_to_select2(type, options)
javascript_tag "setSelect2Filter('#{type}', #{options.to_json});" unless type.empty?
end
end

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
module AdditionalsSettingsHelper
def additionals_settings_tabs
tabs = [{ name: 'general', partial: 'additionals/settings/general', label: :label_general },
@ -13,7 +15,7 @@ module AdditionalsSettingsHelper
tabs
end
def additionals_settings_checkbox(name, options = {})
def additionals_settings_checkbox(name, **options)
active_value = options.delete(:active_value).presence || @settings.present? && @settings[name]
tag_name = options.delete(:tag_name).presence || "settings[#{name}]"
@ -36,50 +38,50 @@ module AdditionalsSettingsHelper
s = [label_tag(tag_name, label_title)]
s << hidden_field_tag(tag_name, 0, id: nil) if !custom_value || value_is_bool
s << check_box_tag(tag_name, value, checked, options)
s << check_box_tag(tag_name, value, checked, **options)
safe_join s
end
def additionals_settings_numberfield(name, options = {})
additionals_settings_input_field :number_field_tag, name, options
def additionals_settings_numberfield(name, **options)
additionals_settings_input_field :number_field_tag, name, **options
end
def additionals_settings_textfield(name, options = {})
additionals_settings_input_field :text_field_tag, name, options
def additionals_settings_textfield(name, **options)
additionals_settings_input_field :text_field_tag, name, **options
end
def additionals_settings_passwordfield(name, options = {})
additionals_settings_input_field :password_field_tag, name, options
def additionals_settings_passwordfield(name, **options)
additionals_settings_input_field :password_field_tag, name, **options
end
def additionals_settings_urlfield(name, options = {})
additionals_settings_input_field :url_field_tag, name, options
def additionals_settings_urlfield(name, **options)
additionals_settings_input_field :url_field_tag, name, **options
end
def additionals_settings_select(name, values, options = {})
def additionals_settings_select(name, values, **options)
tag_name = options.delete(:tag_name).presence || "settings[#{name}]"
label_title = [options.delete(:label).presence || l("label_#{name}")]
label_title << tag.span('*', class: 'required') if options[:required].present?
safe_join [label_tag(tag_name, safe_join(label_title, ' ')),
select_tag(tag_name, values, options)]
select_tag(tag_name, values, **options)]
end
def additionals_settings_textarea(name, options = {})
def additionals_settings_textarea(name, **options)
label_title = options.delete(:label).presence || l("label_#{name}")
value = options.delete(:value).presence || @settings[name]
options[:class] = 'wiki-edit' unless options.key?(:class)
options[:rows] = addtionals_textarea_cols(value) unless options.key?(:rows)
options[:class] = 'wiki-edit' unless options.key? :class
options[:rows] = addtionals_textarea_cols value unless options.key? :rows
safe_join [label_tag("settings[#{name}]", label_title),
text_area_tag("settings[#{name}]", value, options)]
text_area_tag("settings[#{name}]", value, **options)]
end
private
def additionals_settings_input_field(tag_field, name, options = {})
def additionals_settings_input_field(tag_field, name, **options)
tag_name = options.delete(:tag_name).presence || "settings[#{name}]"
value = if options.key? :value
options.delete(:value).presence
@ -91,6 +93,6 @@ module AdditionalsSettingsHelper
label_title << tag.span('*', class: 'required') if options[:required].present?
safe_join [label_tag(tag_name, safe_join(label_title, ' ')),
send(tag_field, tag_name, value, options)], ' '
send(tag_field, tag_name, value, **options)], ' '
end
end

View File

@ -1,22 +1,24 @@
# frozen_string_literal: true
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 = ITCPDF.new current_language
pdf.set_title "#{project} - #{page.title}"
pdf.alias_nb_pages
pdf.footer_date = format_date(User.current.today)
pdf.footer_date = format_date User.current.today
pdf.add_page
unless Additionals.setting?(:wiki_pdf_remove_title)
pdf.SetFontStyle('B', 11)
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.set_image_scale 1.6
pdf.SetFontStyle '', 9
if Additionals.setting? :wiki_pdf_remove_attachments
pdf.RDMwriteFormattedCell(190,
5,
'',
@ -28,7 +30,7 @@ module AdditionalsWikiPdfHelper
headings: false,
inline_attachments: false), page.attachments)
else
write_wiki_page(pdf, page)
write_wiki_page pdf, page
end
pdf.output
end

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
module DashboardsHelper
def dashboard_async_cache(dashboard, block, async, settings, &content_block)
cache render_async_cache_key(_dashboard_async_blocks_path(@project, dashboard.async_params(block, async, settings))),
@ -32,7 +34,7 @@ module DashboardsHelper
def sidebar_dashboards(dashboard, project = nil, user = nil)
user ||= User.current
scope = Dashboard.visible.includes([:author])
scope = Dashboard.visible.includes [:author]
scope = if project.present?
scope = scope.project_only
@ -111,7 +113,7 @@ module DashboardsHelper
return '' unless dashboards.any?
tag.h3(title, class: 'dashboards') +
tag.ul(class: 'dashboards') do
tag.ul(class: 'dashboards') do # rubocop: disable Style/MethodCallWithArgsParentheses
dashboards.each do |dashboard|
selected = dashboard.id == if params[:dashboard_id].present?
params[:dashboard_id].to_i
@ -119,7 +121,7 @@ module DashboardsHelper
active_dashboard.id
end
css = 'dashboard'
css = +'dashboard'
css << ' selected' if selected
li_class = nil
@ -143,14 +145,14 @@ module DashboardsHelper
end
end
def dashboard_link(dashboard, project, options = {})
def dashboard_link(dashboard, project, **options)
if options[:title].blank? && dashboard.public?
author = if dashboard.author_id == User.current.id
l :label_me
else
dashboard.author
end
options[:title] = l(:label_dashboard_author, name: author)
options[:title] = l :label_dashboard_author, name: author
end
name = options.delete(:name) || dashboard.name
@ -171,10 +173,10 @@ module DashboardsHelper
end
end
def delete_dashboard_link(url, options = {})
def delete_dashboard_link(url)
options = { method: :delete,
data: { confirm: l(:text_are_you_sure) },
class: 'icon icon-del' }.merge(options)
class: 'icon icon-del' }
link_to l(:button_dashboard_delete), url, options
end
@ -252,7 +254,7 @@ module DashboardsHelper
exposed_params: %i[sort],
partial: 'dashboards/blocks/query_list' }
partial_locals[:async][:unique_params] = [Redmine::Utils.random_hex(16)] if params[:refresh].present?
partial_locals[:async] = partial_locals[:async].merge(block_definition[:async]) if block_definition[:async]
partial_locals[:async] = partial_locals[:async].merge block_definition[:async] if block_definition[:async]
elsif block_definition[:async]
partial_locals[:async] = block_definition[:async]
end
@ -277,9 +279,9 @@ module DashboardsHelper
title << query_block[:label]
title << if query_block[:with_project]
link_to(query.name, send(query_block[:link_helper], project, query.as_params))
link_to query.name, send(query_block[:link_helper], project, query.as_params)
else
link_to(query.name, send(query_block[:link_helper], query.as_params))
link_to query.name, send(query_block[:link_helper], query.as_params)
end
safe_join title, Additionals::LIST_SEPARATOR
@ -289,9 +291,9 @@ module DashboardsHelper
return if dashboard.visibility == Dashboard::VISIBILITY_PRIVATE
title = if query.visibility == Query::VISIBILITY_PRIVATE
l(:alert_only_visible_by_yourself)
l :alert_only_visible_by_yourself
elsif block_definition.key?(:admin_only) && block_definition[:admin_only]
l(:alert_only_visible_by_admins)
l :alert_only_visible_by_admins
end
return if title.nil?
@ -322,7 +324,7 @@ module DashboardsHelper
max_entries = settings[:max_entries] || DashboardContent::DEFAULT_MAX_ENTRIES
scope = Document.visible
scope = scope.where(project: dashboard.project) if dashboard.project
scope = scope.where project: dashboard.project if dashboard.project
documents = scope.order(created_on: :desc)
.limit(max_entries)
@ -357,10 +359,10 @@ module DashboardsHelper
days = 7 if days < 1 || days > 365
scope = TimeEntry.where user_id: User.current.id
scope = scope.where(project_id: dashboard.content_project.id) unless dashboard.content_project.nil?
scope = scope.where project_id: dashboard.content_project.id unless dashboard.content_project.nil?
entries_today = scope.where(spent_on: User.current.today)
entries_days = scope.where(spent_on: User.current.today - (days - 1)..User.current.today)
entries_today = scope.where spent_on: User.current.today
entries_days = scope.where spent_on: User.current.today - (days - 1)..User.current.today
render partial: 'dashboards/blocks/my_spent_time',
locals: { block: block,
@ -379,7 +381,7 @@ module DashboardsHelper
Redmine::Activity::Fetcher.new(user, options)
.events(nil, nil, limit: max_entries)
.group_by { |event| user.time_to_date(event.event_datetime) }
.group_by { |event| user.time_to_date event.event_datetime }
end
def dashboard_feed_catcher(url, max_entries)
@ -391,7 +393,7 @@ module DashboardsHelper
begin
URI.parse(url).open do |rss_feed|
rss = RSS::Parser.parse(rss_feed)
rss = RSS::Parser.parse rss_feed
rss.items.each do |item|
cnt += 1
feed[:items] << { title: item.title.try(:content)&.presence || item.title,
@ -430,7 +432,7 @@ module DashboardsHelper
# Renders a single block content
def render_dashboard_block_content(block, block_definition, dashboard, overwritten_settings = {})
settings = dashboard.layout_settings block
settings = settings.merge(overwritten_settings) if overwritten_settings.present?
settings = settings.merge overwritten_settings if overwritten_settings.present?
partial = block_definition[:partial]
partial_locals = build_dashboard_partial_locals block, block_definition, settings, dashboard
@ -441,7 +443,7 @@ module DashboardsHelper
begin
render partial: partial, locals: partial_locals
rescue ActionView::MissingTemplate
Rails.logger.warn("Partial \"#{partial}\" missing for block \"#{block}\" found in #{dashboard.name} (id=#{dashboard.id})")
Rails.logger.warn "Partial \"#{partial}\" missing for block \"#{block}\" found in #{dashboard.name} (id=#{dashboard.id})"
nil
end
else

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
class AdditionalsJob < ActiveJob::Base
# Automatically retry jobs that encountered a deadlock
# retry_on ActiveRecord::Deadlocked

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
class AdditionalsChart
include ActiveRecord::Sanitization
include Redmine::I18n
@ -15,7 +17,7 @@ class AdditionalsChart
end
# build return value
def build_chart_data(datasets, options = {})
def build_chart_data(datasets, **options)
cached_labels = labels
data = { datasets: datasets.to_json,
labels: cached_labels.keys,
@ -23,13 +25,13 @@ class AdditionalsChart
required_labels = options.key?(:required_labels) ? options.delete(:required_labels) : 2
data[:valid] = cached_labels.any? && cached_labels.count >= required_labels unless options.key?(:valid)
data[:width] = self::CHART_DEFAULT_WIDTH unless options.key?(:width)
data[:height] = self::CHART_DEFAULT_HEIGHT unless options.key?(:height)
data[:value_link_method] = '_project_issues_path' unless options.key?(:value_link_method)
data[:valid] = cached_labels.any? && cached_labels.count >= required_labels unless options.key? :valid
data[:width] = self::CHART_DEFAULT_WIDTH unless options.key? :width
data[:height] = self::CHART_DEFAULT_HEIGHT unless options.key? :height
data[:value_link_method] = '_project_issues_path' unless options.key? :value_link_method
data[:color_schema] = color_schema
data.merge(options)
data.merge options
end
private
@ -37,7 +39,7 @@ class AdditionalsChart
def build_values_without_gaps(data, gap_value = 0)
values = []
labels.each do |label, _label_id|
values << if data.key?(label)
values << if data.key? label
data[label]
else
gap_value

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
class AdditionalsFontAwesome
include Redmine::I18n
@ -9,7 +11,7 @@ class AdditionalsFontAwesome
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))
icons[key] = { unicode: values['unicode'], label: values['label'] } if values['styles'].include? convert_type2style(type)
end
icons
end
@ -48,7 +50,7 @@ class AdditionalsFontAwesome
end
def classes(value)
info = value_info(value)
info = value_info value
return '' if info.blank?
info[:classes]
@ -65,27 +67,27 @@ class AdditionalsFontAwesome
# 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)
info = value_info selected, with_details: true
return [] if info.blank?
[[info[:label], selected]]
end
def value_info(value, options = {})
def value_info(value, with_details: false)
return {} if value.blank?
values = value.split('_')
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])
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]))
if with_details
info.merge! load_details(info[:type], values[1])
return {} if info[:unicode].blank?
end
@ -94,12 +96,12 @@ class AdditionalsFontAwesome
def search_for_select(search, selected = nil)
# could be more then one
selected_store = selected.to_s.split(',')
icons = search_in_type(:far, search, selected_store)
selected_store = selected.to_s.split ','
icons = search_in_type :far, search, selected_store
cnt = icons.count
return icons if cnt >= SEARCH_LIMIT
icons += search_in_type(:fas, search, selected_store, cnt)
icons += search_in_type :fas, search, selected_store, cnt
cnt = icons.count
return icons if cnt >= SEARCH_LIMIT
@ -109,7 +111,7 @@ class AdditionalsFontAwesome
def convert2mermaid(icon)
return if icon.blank?
parts = icon.split('_')
parts = icon.split '_'
return unless parts.count == 2
"#{parts.first}:fa-#{parts.last}"
@ -125,7 +127,7 @@ class AdditionalsFontAwesome
search[0].downcase
elsif search_length.zero? && selected_store.any?
selected = selected_store.first
fa = selected.split('_')
fa = selected.split '_'
search = fa[1][0] if fa.count > 1
search
end
@ -147,7 +149,7 @@ class AdditionalsFontAwesome
end
def load_details(type, name)
return {} unless FONTAWESOME_ICONS.key?(type)
return {} unless FONTAWESOME_ICONS.key? type
values = FONTAWESOME_ICONS[type][name]
return {} if values.blank?

View File

@ -1,9 +1,11 @@
# frozen_string_literal: true
class AdditionalsImport < Import
class_attribute :import_class
# Returns the objects that were imported
def saved_objects
object_ids = saved_items.pluck(:obj_id)
object_ids = saved_items.pluck :obj_id
import_class.where(id: object_ids).order(:id)
end
@ -24,17 +26,17 @@ class AdditionalsImport < Import
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}")
row_date row, "cf_#{v.custom_field.id}"
else
row_value(row, "cf_#{v.custom_field.id}")
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) }.flatten!&.compact
value.map { |val| v.custom_field.value_from_keyword val.strip, object }.flatten!&.compact
else
v.custom_field.value_from_keyword(value, object)
v.custom_field.value_from_keyword value, object
end
end
end

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
class AdditionalsInfo
include Redmine::I18n
@ -43,11 +45,11 @@ class AdditionalsInfo
days = (secs / 86_400).round
end
if days >= 1
"#{days} #{l(:days, count: days)}"
"#{days} #{l :days, count: days}"
elsif hours >= 1
"#{hours} #{l(:hours, count: hours)}"
"#{hours} #{l :hours, count: hours}"
else
"#{min} #{l(:minutes, count: min)}"
"#{min} #{l :minutes, count: min}"
end
else
# this should be work on macOS
@ -61,7 +63,7 @@ class AdditionalsInfo
end
else
days = `uptime | awk '{print $3}'`.to_i.round
"#{days} #{l(:days, count: days)}"
"#{days} #{l :days, count: days}"
end
end
end

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
class AdditionalsJournal
class << self
def save_journal_history(journal, prop_key, ids_old, ids)
@ -6,7 +8,7 @@ class AdditionalsJournal
ids_all.each do |id|
next if ids_old.include?(id) && ids.include?(id)
if ids.include?(id)
if ids.include? id
value = id
old_value = nil
else
@ -29,10 +31,10 @@ class AdditionalsJournal
new_entries = entries.select { |entry| entry.id.blank? }
return true if new_entries.blank?
new_entries.map! { |entry| entry.send(entry_id) }
new_entries.map! { |entry| entry.send entry_id }
return false if new_entries.count != new_entries.uniq.count
old_entries.map! { |entry| entry.send(entry_id) }
old_entries.map! { |entry| entry.send entry_id }
return false unless (old_entries & new_entries).count.zero?
true

View File

@ -1,75 +1,81 @@
# frozen_string_literal: true
class AdditionalsMacro
def self.all(options = {})
all = Redmine::WikiFormatting::Macros.available_macros
macros = {}
macro_list = []
class << self
def all(only_names: false, filtered: [], controller_only: nil)
all = Redmine::WikiFormatting::Macros.available_macros
macros = {}
macro_list = []
# needs to run every request (for each user once)
permissions = build_permissions(options)
# needs to run every request (for each user once)
permissions = build_permissions controller_only: controller_only
if options[:filtered].present?
options[:filtered] << 'hello_world'
else
options[:filtered] = ['hello_world']
if filtered.present?
filtered << 'hello_world'
else
filtered = ['hello_world']
end
all.each do |macro, macro_options|
next if filtered.include? macro.to_s
next unless macro_allowed macro, permissions
macro_list << macro.to_s
macros[macro] = macro_options
end
if only_names
macro_list.sort
else
macros.sort
end
end
all.each do |macro, macro_options|
next if options[:filtered].include?(macro.to_s)
next unless macro_allowed(macro, permissions)
private
macro_list << macro.to_s
macros[macro] = macro_options
def macro_allowed(macro, permissions)
permissions.each do |permission|
next if permission[:list].exclude? macro
return false unless permission[:access]
end
true
end
if options[:only_names]
macro_list.sort
else
macros.sort
end
end
def build_permissions(controller_only: nil)
gpermission = []
macro_permissions.each do |permission|
permission[:access] = if controller_only &&
permission[:controller].present? &&
controller_only.to_sym != permission[:controller]
false
else
User.current.allowed_to? permission[:permission], nil, global: true
end
gpermission << permission
end
def self.macro_allowed(macro, permissions)
permissions.each do |permission|
next if permission[:list].exclude?(macro)
return false unless permission[:access]
gpermission
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
def 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 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
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 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

View File

@ -1,17 +1,19 @@
# frozen_string_literal: true
module AdditionalsQuery
def column_with_prefix?(prefix)
columns.detect { |c| c.name.to_s.start_with?("#{prefix}.") }.present?
columns.detect { |c| c.name.to_s.start_with? "#{prefix}." }.present?
end
def available_column_names(options = {})
def available_column_names(only_sortable: false)
names = available_columns.dup
names.flatten!
names.select! { |col| col.sortable.present? } if options[:only_sortable]
names.select! { |col| col.sortable.present? } if only_sortable
names.map(&:name)
end
def sql_for_enabled_module(table_field, module_names)
module_names = Array(module_names)
module_names = Array module_names
sql = []
module_names.each do |module_name|
@ -19,7 +21,7 @@ module AdditionalsQuery
" AND #{EnabledModule.table_name}.name='#{module_name}')"
end
sql.join(' AND ')
sql.join ' AND '
end
def fix_sql_for_text_field(field, operator, value, table_name = nil, target_field = nil)
@ -27,15 +29,15 @@ module AdditionalsQuery
target_field = field if target_field.blank?
sql = []
sql << "(#{sql_for_field(field, operator, value, table_name, target_field)})"
sql << "(#{sql_for_field field, operator, value, table_name, target_field})"
sql << "#{table_name}.#{target_field} != ''" if operator == '*'
sql.join(' AND ')
sql.join ' AND '
end
def initialize_ids_filter(options = {})
if options[:label]
add_available_filter 'ids', type: :integer, label: options[:label]
def initialize_ids_filter(label: nil)
if label
add_available_filter 'ids', type: :integer, label: label
else
add_available_filter 'ids', type: :integer, name: '#'
end
@ -82,35 +84,29 @@ module AdditionalsQuery
values: -> { project_statuses_values }
end
def initialize_project_filter(options = {})
if project.nil? || options[:always]
add_available_filter 'project_id', order: options[:position],
def initialize_project_filter(always: false, position: nil)
if project.nil? || always
add_available_filter 'project_id', order: position,
type: :list,
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: position,
type: :list_subprojects,
values: -> { subproject_values }
end
def initialize_created_filter(options = {})
add_available_filter 'created_on', order: options[:position],
def initialize_created_filter(position: nil, label: nil)
add_available_filter 'created_on', order: position,
type: :date_past,
label: options[:label].presence
label: label
end
def initialize_updated_filter(options = {})
add_available_filter 'updated_on', order: options[:position],
def initialize_updated_filter(position: nil, label: nil)
add_available_filter 'updated_on', order: position,
type: :date_past,
label: options[:label].presence
end
def initialize_tags_filter(options = {})
add_available_filter 'tags', order: options[:position],
type: :list_optional,
values: -> { tag_values(project) }
label: label
end
def initialize_approved_filter
@ -123,38 +119,26 @@ module AdditionalsQuery
label: :field_approved
end
def initialize_author_filter(options = {})
add_available_filter 'author_id', order: options[:position],
def initialize_author_filter(position: nil)
add_available_filter 'author_id', order: position,
type: :list_optional,
values: -> { author_values }
end
def initialize_assignee_filter(options = {})
add_available_filter 'assigned_to_id', order: options[:position],
def initialize_assignee_filter(position: nil)
add_available_filter 'assigned_to_id', order: position,
type: :list_optional,
values: -> { assigned_to_all_values }
end
def initialize_watcher_filter(options = {})
def initialize_watcher_filter(position: nil)
return unless User.current.logged?
add_available_filter 'watcher_id', order: options[:position],
add_available_filter 'watcher_id', order: position,
type: :list,
values: -> { watcher_values_for_manage_public_queries }
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
def assigned_to_all_values
assigned_to_values = []
@ -168,7 +152,7 @@ module AdditionalsQuery
# and with users (not groups)
def watcher_values_for_manage_public_queries
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 += users.collect { |s| [s.name, s.id.to_s] } if User.current.allowed_to? :manage_public_queries, project, global: true
watcher_values
end
@ -181,12 +165,8 @@ module AdditionalsQuery
" #{sql_for_field field, '=', value, db_table, 'user_id'})"
end
def sql_for_tags_field(field, _operator, value)
AdditionalTags.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)
if bool_operator operator, value
return '' if value.count > 1
"#{queried_table_name}.is_private = #{self.class.connection.quoted_true}"
@ -215,10 +195,10 @@ module AdditionalsQuery
raise ::Query::StatementInvalid, e.message
end
def results_scope(options = {})
def results_scope(**options)
order_option = [group_by_sort_order, (options[:order] || sort_clause)].flatten!.to_a.reject(&:blank?)
objects_scope(options)
objects_scope(**options.except(:order, :limit, :offset))
.order(order_option)
.joins(joins_for_order_statement(order_option.join(',')))
.limit(options[:limit])

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
class Dashboard < ActiveRecord::Base
include Redmine::I18n
include Redmine::SafeAttributes
@ -22,10 +24,10 @@ class Dashboard < ActiveRecord::Base
VISIBILITY_ROLES = 1
VISIBILITY_PUBLIC = 2
scope :by_project, (->(project_id) { where(project_id: project_id) if project_id.present? })
scope :sorted, (-> { order("#{Dashboard.table_name}.name") })
scope :welcome_only, (-> { where(dashboard_type: DashboardContentWelcome::TYPE_NAME) })
scope :project_only, (-> { where(dashboard_type: DashboardContentProject::TYPE_NAME) })
scope :by_project, (->(project_id) { where project_id: project_id if project_id.present? })
scope :sorted, (-> { order "#{Dashboard.table_name}.name" })
scope :welcome_only, (-> { where dashboard_type: DashboardContentWelcome::TYPE_NAME })
scope :project_only, (-> { where dashboard_type: DashboardContentProject::TYPE_NAME })
safe_attributes 'name', 'description', 'enable_sidebar',
'always_expose', 'project_id', 'author_id',
@ -47,7 +49,7 @@ class Dashboard < ActiveRecord::Base
safe_attributes 'system_default',
if: (lambda do |dashboard, user|
user.allowed_to?(:set_system_dashboards, dashboard.project, global: true)
user.allowed_to? :set_system_dashboards, dashboard.project, global: true
end)
before_save :dashboard_type_check, :visibility_check, :set_options_hash, :clear_unused_block_settings
@ -73,10 +75,10 @@ class Dashboard < ActiveRecord::Base
def default(dashboard_type, project = nil, user = User.current)
recently_id = User.current.pref.recently_used_dashboard dashboard_type, project
scope = where(dashboard_type: dashboard_type)
scope = where dashboard_type: dashboard_type
scope = scope.where(project_id: project.id).or(scope.where(project_id: nil)) if project.present?
dashboard = scope.visible.find_by(id: recently_id) if recently_id.present?
dashboard = scope.visible.find_by id: recently_id if recently_id.present?
if dashboard.blank?
scope = scope.where(system_default: true).or(scope.where(author_id: user.id))
@ -86,7 +88,7 @@ class Dashboard < ActiveRecord::Base
Rails.logger.debug 'default cleanup required'
# Remove invalid recently_id
if project.present?
User.current.pref.recently_used_dashboards[dashboard_type].delete(project.id)
User.current.pref.recently_used_dashboards[dashboard_type].delete project.id
else
User.current.pref.recently_used_dashboards[dashboard_type] = nil
end
@ -101,7 +103,7 @@ class Dashboard < ActiveRecord::Base
["#{table}.name"]
end
def visible(user = User.current, options = {})
def visible(user = User.current, **options)
scope = left_outer_joins :project
scope = scope.where(projects: { id: nil }).or(scope.where(Project.allowed_to_condition(user, :view_project, options)))
@ -152,7 +154,7 @@ class Dashboard < ActiveRecord::Base
super
else
h = (self[:options] || {}).dup
h.update(attr_name => value)
h.update attr_name => value
self[:options] = h
value
end
@ -236,7 +238,7 @@ class Dashboard < ActiveRecord::Base
return if content.groups.exclude?(group) || blocks.blank?
blocks = blocks.map(&:underscore) & layout.values.flatten
blocks.each { |block| remove_block(block) }
blocks.each { |block| remove_block block }
layout[group] = blocks
end
@ -261,7 +263,7 @@ class Dashboard < ActiveRecord::Base
end
def editable?(usr = User.current)
@editable ||= editable_by?(usr)
@editable ||= editable_by? usr
end
def destroyable_by?(usr = User.current)
@ -274,7 +276,7 @@ class Dashboard < ActiveRecord::Base
end
def destroyable?
@destroyable ||= destroyable_by?(User.current)
@destroyable ||= destroyable_by? User.current
end
def to_s
@ -285,7 +287,7 @@ class Dashboard < ActiveRecord::Base
def css_classes(user = User.current)
s = ['dashboard']
s << 'created-by-me' if author_id == user.id
s.join(' ')
s.join ' '
end
def allowed_target_projects(user = User.current)
@ -293,7 +295,7 @@ class Dashboard < ActiveRecord::Base
end
# this is used to get unique cache for blocks
def async_params(block, options, settings = {})
def async_params(block, options, settings)
if block.blank?
msg = 'block is missing for dashboard_async'
Rails.log.error msg
@ -326,7 +328,7 @@ class Dashboard < ActiveRecord::Base
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}"
config[:unique_key] = Digest::SHA256.hexdigest(unique_params.join('_'))
config[:unique_key] = Digest::SHA256.hexdigest unique_params.join('_')
end
# Rails.logger.debug "debug async_params for #{block}: config=#{config.inspect}"
@ -345,7 +347,7 @@ class Dashboard < ActiveRecord::Base
def clear_unused_block_settings
blocks = layout.values.flatten
layout_settings.keep_if { |block, _settings| blocks.include?(block) }
layout_settings.keep_if { |block, _settings| blocks.include? block }
end
def remove_unused_role_relations
@ -391,7 +393,7 @@ class Dashboard < ActiveRecord::Base
.where(dashboard_type: dashboard_type)
.where.not(id: id)
scope = scope.where(project: project) if dashboard_type == DashboardContentProject::TYPE_NAME
scope = scope.where project: project if dashboard_type == DashboardContentProject::TYPE_NAME
scope.update_all system_default: false
end
@ -409,22 +411,22 @@ class Dashboard < ActiveRecord::Base
end
def validate_visibility
errors.add(:visibility, :must_be_for_everyone) if system_default? && visibility != VISIBILITY_PUBLIC
errors.add :visibility, :must_be_for_everyone if system_default? && visibility != VISIBILITY_PUBLIC
end
def validate_name
return if name.blank?
scope = self.class.visible.where(name: name)
scope = self.class.visible.where name: name
if dashboard_type == DashboardContentProject::TYPE_NAME
scope = scope.project_only
scope = scope.where project_id: project_id
scope = scope.or(scope.where(project_id: nil)) if project_id.present?
scope = scope.or scope.where(project_id: nil) if project_id.present?
else
scope = scope.welcome_only
end
scope = scope.where.not(id: id) unless new_record?
errors.add(:name, :name_not_unique) if scope.count.positive?
scope = scope.where.not id: id unless new_record?
errors.add :name, :name_not_unique if scope.count.positive?
end
end

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
class DashboardContent
include Redmine::I18n
@ -100,7 +102,7 @@ class DashboardContent
def find_block(block)
block.to_s =~ /\A(.*?)(__\d+)?\z/
name = Regexp.last_match(1)
name = Regexp.last_match 1
available_blocks.key?(name) ? available_blocks[name].merge(name: name) : nil
end

View File

@ -1,5 +1,7 @@
# frozen_string_literal: true
class DashboardContentProject < DashboardContent
TYPE_NAME = 'ProjectDashboard'.freeze
TYPE_NAME = 'ProjectDashboard'
def block_definitions
blocks = super

View File

@ -1,5 +1,7 @@
# frozen_string_literal: true
class DashboardContentWelcome < DashboardContent
TYPE_NAME = 'WelcomeDashboard'.freeze
TYPE_NAME = 'WelcomeDashboard'
def block_definitions
blocks = super

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
class DashboardRole < ActiveRecord::Base
include Redmine::SafeAttributes

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
Deface::Override.new virtual_path: 'admin/info',
name: 'add-system_info',
insert_after: 'table.list',

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
unless Redmine::Plugin.installed? 'redmine_servicedesk'
if defined?(CONTACTS_VERSION_TYPE) && CONTACTS_VERSION_TYPE == 'PRO version'
Deface::Override.new virtual_path: 'contacts/_form',

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
Deface::Override.new virtual_path: 'custom_fields/formats/_text',
name: 'custom_fields-formats-text',
replace: 'erb[silent]:contains(\'if @custom_field.class.name == "IssueCustomField"\')',

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
Deface::Override.new virtual_path: 'issues/_edit',
name: 'edit-issue-permission',
replace: 'erb[silent]:contains("User.current.allowed_to?(:log_time, @project)")',

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
Deface::Override.new virtual_path: 'issues/_list',
name: 'list-issue-back-url',
replace: 'erb[loud]:contains("hidden_field_tag \'back_url\'")',

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
Deface::Override.new virtual_path: 'issues/_action_menu',
name: 'show-issue-log-time',
replace: 'erb[loud]:contains("User.current.allowed_to?(:log_time, @project)")',

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
Deface::Override.new virtual_path: 'reports/_simple',
name: 'report-simple-user-scope',
insert_before: 'erb[silent]:contains("rows.empty?")',

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
Deface::Override.new virtual_path: 'roles/_form',
name: 'roles-form-hide',
insert_before: 'p.manage_members_shown',

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
unless Redmine::Plugin.installed? 'redmine_hrm'
if Redmine::VERSION.to_s >= '4.2'
Deface::Override.new virtual_path: 'users/show',

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
Deface::Override.new virtual_path: 'wiki/edit',
name: 'wiki-edit-bottom',
insert_before: 'fieldset',

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
Deface::Override.new virtual_path: 'wiki/show',
name: 'wiki-show-bottom',
insert_before: 'p.wiki-update-info',

View File

@ -1,32 +1,32 @@
div id="#{export_format}-export-options" style="display: none"
h3.title = l(:label_export_options, export_format: export_format.upcase)
h3.title = l :label_export_options, export_format: export_format.upcase
= form_tag(url, method: :get, id: "#{export_format}-export-form") do
= form_tag url, method: :get, id: "#{export_format}-export-form" do
= query_as_hidden_field_tags @query
- if defined?(selected_columns_only) && selected_columns_only
= hidden_field_tag 'c[]', ''
= l(:description_selected_columns)
= l :description_selected_columns
- else
p
label
= radio_button_tag 'c[]', '', true
= l(:description_selected_columns)
= l :description_selected_columns
br
label
= radio_button_tag 'c[]', 'all_inline'
= l(:description_all_columns)
= l :description_all_columns
hr
- if @query.available_filters.key?('description')
- if @query.available_filters.key? 'description'
p
label
= check_box_tag 'c[]', 'description', @query.has_column?(:description)
= l(:field_description)
= l :field_description
- if defined?(with_last_notes) && with_last_notes
label
= check_box_tag 'c[]', 'last_notes', @query.has_column?(:last_notes)
= l(:label_last_notes)
= l :label_last_notes
= export_csv_encoding_select_tag

View File

@ -1,18 +1,18 @@
fieldset.box
legend = l(:additionals_query_list_defaults)
legend = l :additionals_query_list_defaults
- setting_name_columns = "#{query_type}_list_defaults"
- query = query_class.new(@settings[setting_name_columns.to_sym])
- query = query_class.new @settings[setting_name_columns.to_sym]
.default-query-settings-label
= render_query_columns_selection(query, name: "settings[#{setting_name_columns}][column_names]")
= render_query_columns_selection query, name: "settings[#{setting_name_columns}][column_names]"
- columns = query_class.new.available_totalable_columns
- if columns.count.positive?
fieldset.box
legend = l(:additionals_query_list_default_totals)
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}][]", '')
= 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

View File

@ -1,47 +1,47 @@
fieldset.settings
legend = l(:label_content_plural)
legend = l :label_content_plural
p
= additionals_settings_textarea :account_login_bottom
em.info
= l(:account_login_info)
= l :account_login_info
p
= additionals_settings_textarea :global_sidebar
em.info
= l(:global_sidebar_info)
= l :global_sidebar_info
p
= additionals_settings_textarea :global_footer
em.info
= l(:global_footer_info)
= l :global_footer_info
fieldset.settings
legend = l(:label_settings)
legend = l :label_settings
p
= additionals_settings_checkbox :open_external_urls
em.info
= t(:open_external_urls_info)
= t :open_external_urls_info
p
= additionals_settings_checkbox :add_go_to_top
em.info
= t(:add_go_to_top_info)
= t :add_go_to_top_info
p
= additionals_settings_checkbox :legacy_smiley_support
em.info
= t(:legacy_smiley_support_info_html)
= t :legacy_smiley_support_info_html
fieldset.settings
legend = l(:label_disabled_modules)
legend = l :label_disabled_modules
p
= tag.label l(:label_disabled_modules)
= hidden_field_tag('settings[disabled_modules][]', '')
= hidden_field_tag 'settings[disabled_modules][]', ''
- Redmine::AccessControl.available_project_modules_all.sort.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_')
= check_box_tag 'settings[disabled_modules][]', m, value, id: nil
= l_or_humanize m, prefix: 'project_module_'
br
em.info
= l(:disabled_modules_info)
= l :disabled_modules_info

View File

@ -38,7 +38,7 @@ p
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)
em.info = t :rule_issue_status_change_info
br
br
@ -51,7 +51,7 @@ p
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)
em.info = t :rule_issue_current_user_status_info_html
br
br
@ -77,7 +77,7 @@ br
p
= additionals_settings_checkbox :issue_timelog_required
span[style="vertical-align: top; margin-left: 15px;"]
= l(:label_tracker_plural)
= l :label_tracker_plural
| :
= select_tag 'settings[issue_timelog_required_tracker]',
options_for_select(Tracker.all.sorted.collect { |column| [column.name, column.id] },

View File

@ -1,15 +1,15 @@
em.info
= l(:hidden_macros_in_toolbar_info)
= l :hidden_macros_in_toolbar_info
br
p
= tag.label l(:label_hidden_macros_in_toolbar)
= hidden_field_tag('settings[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)
= check_box_tag 'settings[hidden_macros_in_toolbar][]', m, value, id: nil
= m
br

View File

@ -20,14 +20,14 @@ h3 = l :label_custom_menu_items
p
label = h l(:label_permissions)
- permission_field = "custom_menu#{i}_roles"
- menu_roles = Struct.new(:id, :name)
- 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) },
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)
em.info = l :menu_roles_info
br

View File

@ -1,5 +1,5 @@
p
= additionals_settings_textfield :google_maps_api_key, size: 60
em.info = t(:google_maps_embed_api_html)
em.info = t :google_maps_embed_api_html
= call_hook :additionals_settings_web_apis, settings: @settings

View File

@ -1,23 +1,23 @@
em.info = t(:top_wiki_help)
em.info = t :top_wiki_help
br
fieldset.settings
legend = l(:label_content_plural)
legend = l :label_content_plural
p
= additionals_settings_textarea :global_wiki_sidebar
em.info
= l(:global_wiki_sidebar_info)
= l :global_wiki_sidebar_info
fieldset.settings
legend = l(:label_pdf_wiki_settings)
legend = l :label_pdf_wiki_settings
p
= additionals_settings_checkbox :wiki_pdf_remove_title
em.info
= l(:wiki_pdf_remove_title_info)
= l :wiki_pdf_remove_title_info
p
= additionals_settings_checkbox :wiki_pdf_remove_attachments
em.info
= l(:wiki_pdf_remove_attachments_info)
= l :wiki_pdf_remove_attachments_info

View File

@ -1,6 +1,6 @@
= call_hook :view_dashboard_top, dashboard: dashboard, project: @project
#my-page.splitcontent class="#{dashboard_css_classes(dashboard)}"
#my-page.splitcontent class="#{dashboard_css_classes dashboard}"
- dashboard.available_groups.each do |group|
.block-receiver id="list-#{group}" class="splitcontent#{group}"
= render_dashboard_blocks dashboard.layout[group], dashboard

View File

@ -19,7 +19,7 @@
{},
disabled: !@dashboard.project_id_can_change?
em.info
= l(:info_dashboard_project_select)
= l :info_dashboard_project_select
- else
= hidden_field_tag 'dashboard[project_id]', @project&.id
@ -27,19 +27,19 @@
User.current.allowed_to?(:set_system_dashboards, @project, global: true)
p
label = l(:field_visible)
label = l :field_visible
label.block
= radio_button 'dashboard', 'visibility', Dashboard::VISIBILITY_PRIVATE
'
= l(:label_visibility_private)
= l :label_visibility_private
label.block
= radio_button 'dashboard', 'visibility', Dashboard::VISIBILITY_PUBLIC
'
= l(:label_visibility_public)
= l :label_visibility_public
label.block
= radio_button 'dashboard', 'visibility', Dashboard::VISIBILITY_ROLES
'
= l(:label_visibility_roles)
= l :label_visibility_roles
' :
- Role.givable.sorted.each do |role|
label.block.role-visibility

View File

@ -11,7 +11,7 @@ h3 = block_definition[:label]
= l :field_homepage
' :
= link_to_if uri_with_safe_scheme?(@project.homepage), @project.homepage, @project.homepage, class: 'external'
- render_custom_field_values(@project) do |custom_field, formatted|
- render_custom_field_values @project do |custom_field, formatted|
li class="#{custom_field.css_classes}"
span.label
= custom_field.name

View File

@ -1,6 +1,6 @@
- dashboard_async_cache dashboard, block, async, settings do
- query = klass.visible.find_by(id: settings[:query_id])
- query = klass.visible.find_by id: settings[:query_id]
- if query
ruby:
query.project = @project if query_block[:with_project]

View File

@ -1,4 +1,4 @@
h2 = l(:button_dashboard_edit)
h2 = l :button_dashboard_edit
= labelled_form_for :dashboard,
@dashboard,
html: { multipart: true, id: 'dashboard-form' } do |f|

View File

@ -1,4 +1,4 @@
h2 = l(:label_new_additional_dashboard)
h2 = l :label_new_additional_dashboard
= labelled_form_for :dashboard,
@dashboard,
url: { action: 'create', project_id: @project },

View File

@ -1,5 +1,5 @@
- if show_issue_change_author?(issue) && issue.safe_attribute?('author_id')
- author_options = author_options_for_select(issue.project, issue)
- author_options = author_options_for_select issue.project, issue
- if author_options.present?
p#change_author
= form.label_for_field :author_id

View File

@ -1,7 +1,7 @@
- if @project && User.current.allowed_to?(:edit_issue_author, @project)
- author_options = author_options_for_select(@project)
- author_options = 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]',
tag.option(l(:label_no_change_option), value: '') + author_options)
= label_tag 'issue[author_id]', l(:field_author)
= select_tag 'issue[author_id]',
tag.option(l(:label_no_change_option), value: '') + author_options

View File

@ -16,7 +16,7 @@
= actions_dropdown do
- if User.current.allowed_to? :add_subprojects, @project
= link_to l(:label_subproject_new), new_project_path(parent_id: @project), class: 'icon icon-add'
- if User.current.allowed_to?(:close_project, @project)
- if User.current.allowed_to? :close_project, @project
- if @project.active?
= link_to l(:button_close),
close_project_path(@project),
@ -46,14 +46,13 @@
class: 'icon icon-add new-additionals-dashboard'
- if @dashboard&.destroyable?
= delete_dashboard_link project_dashboard_path(@project, @dashboard),
class: 'icon icon-del'
= delete_dashboard_link project_dashboard_path(@project, @dashboard)
= sidebar_action_toggle @dashboard_sidebar, @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)
- if User.current.allowed_to? :edit_project, @project
= link_to_if_authorized l(:label_settings),
{ controller: 'projects', action: 'settings', id: @project },
class: 'icon icon-settings'
@ -63,7 +62,7 @@ h2 = project_overview_name @project, @dashboard
- unless @project.active?
p.warning
span.icon.icon-lock
= l(:text_project_closed)
= l :text_project_closed
= render partial: 'common/dashboard', locals: { dashboard: @dashboard }

View File

@ -9,5 +9,5 @@ tr.group.open
span.badge.badge-count.count = group_count
'
span.totals = group_totals
= link_to_function("#{l :button_collapse_all}/#{l :button_expand_all}",
'toggleAllRowGroups(this)', class: 'toggle-all')
= link_to_function "#{l :button_collapse_all}/#{l :button_expand_all}",
'toggleAllRowGroups(this)', class: 'toggle-all'

View File

@ -1,7 +1,7 @@
p
= f.check_box :hide, disabled: @role.users_visibility != 'members_of_visible_projects'
em.info
= t(:info_hidden_roles_html)
= t :info_hidden_roles_html
javascript:
$(function() {

View File

@ -18,8 +18,7 @@
new_dashboard_path,
class: 'icon icon-add new-additionals-dashboard'
- if @dashboard&.destroyable?
= delete_dashboard_link dashboard_path(@dashboard),
class: 'icon icon-del'
= delete_dashboard_link dashboard_path(@dashboard)
= sidebar_action_toggle @dashboard_sidebar, @dashboard
= render_dashboard_actionlist @dashboard unless @dashboard_sidebar

View File

@ -3,7 +3,7 @@
h3 = list_title
table.list.projects
- project_tree(@projects, init_level: false) do |project, level|
- project_tree @projects, init_level: false do |project, level|
tr id="project-#{project.id}" class="#{project_list_css_classes project, level}"
td.name
span[style='font-weight: bold;']
@ -13,7 +13,7 @@
= link_to_project project
- if project.homepage?
' :
= link_to(project.homepage, project.homepage, @html_options)
= 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),

View File

@ -4,7 +4,7 @@
- users.each do |user|
.user.box class="#{cycle 'odd', 'even'}"
div[style="float: left; display: block; margin-right: 5px;"]
= avatar(user, size: 50)
= avatar user, size: 50
.user.line[style="font-weight: bold;"]
= link_to_user user

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
Rails.application.routes.draw do
resources :issues, only: [] do
resource 'assign_to_me', only: %i[update], controller: 'additionals_assign_to_me'

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
# see https://github.com/nagix/chartjs-plugin-colorschemes
# see https://nagix.github.io/chartjs-plugin-colorschemes/colorchart.html
#
@ -16,8 +18,8 @@ colorschemes_files = [{ name: 'brewer', src: "#{working_path}/src/colorschemes/c
FileUtils.rm_rf working_path
system "git clone https://github.com/nagix/chartjs-plugin-colorschemes.git #{working_path}"
File.open(target_file, 'w') do |file|
file.write("---\n")
File.open target_file, 'w' do |file|
file.write "---\n"
colorschemes_files.each do |color_scheme_file|
file.write "#{color_scheme_file[:name].capitalize}:\n"
File.readlines(color_scheme_file[:src]).each do |line|

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
class AddAutowatchInvolvedIssueToUser < ActiveRecord::Migration[4.2]
def change
add_column :user_preferences, :autowatch_involved_issue, :boolean, default: true, null: false

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
class AddHideToRoles < ActiveRecord::Migration[4.2]
def change
add_column :roles, :hide, :boolean, default: false, null: false

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
class CreateDashboards < ActiveRecord::Migration[5.2]
def change
create_table :dashboards do |t|

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
class CreateDashboardRoles < ActiveRecord::Migration[5.2]
def change
create_table :dashboard_roles do |t|

View File

@ -1,8 +1,10 @@
# frozen_string_literal: true
class CreateDashboardDefaults < ActiveRecord::Migration[5.2]
def up
User.current = User.find_by(id: ENV['DEFAULT_USER_ID'].presence || User.admin.active.first.id)
unless Dashboard.exists?(dashboard_type: DashboardContentWelcome::TYPE_NAME)
unless Dashboard.exists? dashboard_type: DashboardContentWelcome::TYPE_NAME
Dashboard.create! name: 'Welcome dashboard',
dashboard_type: DashboardContentWelcome::TYPE_NAME,
system_default: true,
@ -10,7 +12,7 @@ class CreateDashboardDefaults < ActiveRecord::Migration[5.2]
visibility: 2
end
return if Dashboard.exists?(dashboard_type: DashboardContentProject::TYPE_NAME)
return if Dashboard.exists? dashboard_type: DashboardContentProject::TYPE_NAME
Dashboard.create! name: 'Project dashboard',
dashboard_type: DashboardContentProject::TYPE_NAME,

View File

@ -24,13 +24,13 @@ master_doc = 'index'
# General information about the project.
project = u'additionals'
copyright = u'2013-2020, AlphaNodes GmbH'
copyright = u'2013-2021, AlphaNodes GmbH'
author = u'Alexander Meindl'
# The short X.Y version.
version = u'3.0.0'
version = u'3.0.3'
# The full version, including alpha/beta/rc tags.
release = u'3.0.0'
release = u'3.0.3'
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
Redmine::Plugin.register :additionals do
name 'Additionals'
author 'AlphaNodes GmbH'
@ -14,7 +16,7 @@ Redmine::Plugin.register :additionals do
default_settings["custom_menu#{i}_title"] = ''
end
settings(default: default_settings, partial: 'additionals/settings/additionals')
settings default: default_settings, partial: 'additionals/settings/additionals'
permission :show_hidden_roles_in_memberbox, {}
permission :set_system_dashboards,
@ -56,7 +58,7 @@ end
Rails.application.paths['app/overrides'] ||= []
Dir.glob(Rails.root.join('plugins/*/app/overrides')).each do |dir|
Rails.application.paths['app/overrides'] << dir unless Rails.application.paths['app/overrides'].include?(dir)
Rails.application.paths['app/overrides'] << dir unless Rails.application.paths['app/overrides'].include? dir
end
Rails.configuration.to_prepare do

View File

@ -1,11 +1,13 @@
# frozen_string_literal: true
require 'additionals/version'
module Additionals
MAX_CUSTOM_MENU_ITEMS = 5
SELECT2_INIT_ENTRIES = 20
DEFAULT_MODAL_WIDTH = '350px'.freeze
GOTO_LIST = " \xc2\xbb".freeze
LIST_SEPARATOR = "#{GOTO_LIST} ".freeze
DEFAULT_MODAL_WIDTH = '350px'
GOTO_LIST = " \xc2\xbb"
LIST_SEPARATOR = "#{GOTO_LIST} "
class << self
def setup
@ -27,6 +29,7 @@ module Additionals
ReportsController
Principal
QueryFilter
QueriesHelper
Role
User
UserPreference]
@ -95,7 +98,8 @@ module Additionals
def debug(message)
return if Rails.env.production?
Rails.logger.debug "#{Time.current.strftime('%H:%M:%S')} DEBUG [#{caller_locations(1..1).first.label}]: #{message}"
msg = message.is_a?(String) ? message : message.inspect
Rails.logger.debug "#{Time.current.strftime '%H:%M:%S'} DEBUG [#{caller_locations(1..1).first.label}]: #{msg}"
end
def class_prefix(klass)
@ -113,19 +117,19 @@ module Additionals
def incompatible_plugins(plugins = [], title = 'additionals')
plugins.each do |plugin|
raise "\n\033[31m#{title} plugin cannot be used with #{plugin} plugin.\033[0m" if Redmine::Plugin.installed?(plugin)
raise "\n\033[31m#{title} plugin cannot be used with #{plugin} plugin.\033[0m" if Redmine::Plugin.installed? plugin
end
end
def patch(patches = [], plugin_id = 'additionals')
patches.each do |name|
patch_dir = Rails.root.join("plugins/#{plugin_id}/lib/#{plugin_id}/patches")
patch_dir = Rails.root.join "plugins/#{plugin_id}/lib/#{plugin_id}/patches"
require "#{patch_dir}/#{name.underscore}_patch"
target = name.constantize
patch = "#{plugin_id.camelize}::Patches::#{name}Patch".constantize
target.include(patch) unless target.included_modules.include?(patch)
target.include patch unless target.included_modules.include? patch
end
end

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
module Additionals
module EntityMethods
def assignable_users(prj = nil)

View File

@ -1,4 +1,5 @@
# Formater
# frozen_string_literal: true
module Additionals
module Formatter
SMILEYS = { 'smiley' => ':-?\)', # :)
@ -27,16 +28,16 @@ module Additionals
def render_inline_smileys(text)
return text if text.blank?
inline_smileys(text)
inline_smileys text
text
end
def inline_smileys(text)
SMILEYS.each do |name, regexp|
text.gsub!(/(\s|^|>|\))(!)?(#{regexp})(?=\W|$|<)/m) do
leading = Regexp.last_match(1)
esc = Regexp.last_match(2)
smiley = Regexp.last_match(3)
leading = Regexp.last_match 1
esc = Regexp.last_match 2
smiley = Regexp.last_match 3
if esc.nil?
leading + tag.span(class: "additionals smiley smiley-#{name}",
title: smiley)
@ -49,8 +50,8 @@ module Additionals
def inline_emojify(text)
text.gsub!(/:([\w+-]+):/) do |match|
emoji_code = Regexp.last_match(1)
emoji = Emoji.find_by_alias(emoji_code) # rubocop:disable Rails/DynamicFindBy
emoji_code = Regexp.last_match 1
emoji = Emoji.find_by_alias emoji_code # rubocop:disable Rails/DynamicFindBy
if emoji.present?
tag.img src: inline_emojify_image_path(emoji.image_filename),
title: ":#{emoji_code}:",

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
module Additionals
class Gemify
class << self
@ -10,11 +12,11 @@ module Additionals
return unless File.directory? source
source_files = Dir["#{source}/**/*"]
source_dirs = source_files.select { |d| File.directory?(d) }
source_dirs = source_files.select { |d| File.directory? d }
source_files -= source_dirs
unless source_files.empty?
base_target_dir = File.join(destination, File.dirname(source_files.first).gsub(source, ''))
base_target_dir = File.join destination, File.dirname(source_files.first).gsub(source, '')
begin
FileUtils.mkdir_p base_target_dir
rescue StandardError => e
@ -23,9 +25,9 @@ module Additionals
end
source_dirs.each do |dir|
target_dir = File.join(destination, dir.gsub(source, ''))
target_dir = File.join destination, dir.gsub(source, '')
begin
FileUtils.mkdir_p(target_dir)
FileUtils.mkdir_p target_dir
rescue StandardError => e
raise "Could not create directory #{target_dir}: " + e.message
end
@ -33,7 +35,7 @@ module Additionals
source_files.each do |file|
target = File.join destination, file.gsub(source, '')
FileUtils.cp(file, target) unless File.exist?(target) && FileUtils.identical?(file, target)
FileUtils.cp file, target unless File.exist?(target) && FileUtils.identical?(file, target)
rescue StandardError => e
raise "Could not copy #{file} to #{target}: " + e.message
end
@ -42,12 +44,12 @@ module Additionals
# Create text file to Redmine's plugins directory.
# The purpose is telling plugins directory to users.
def create_plugin_hint(plugin_id = 'additionals')
plugins_dir = File.join(Bundler.root, 'plugins')
plugins_dir = File.join Bundler.root, 'plugins'
path = File.join plugins_dir, plugin_id
return if File.exist? path
File.open(path, 'w') do |f|
f.write(<<PLUGIN_HINT)
File.open path, 'w' do |f|
f.write <<PLUGIN_HINT
This plugin was installed as gem wrote to Gemfile.local instead of putting Redmine's plugin directory.
See #{plugin_id} gem installed directory.
PLUGIN_HINT

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
module Additionals
module Helpers
def live_search_title_info(entity)
@ -6,7 +8,7 @@ module Additionals
l :label_live_search_hints, value: all_fields
end
def link_to_external(name, link, options = {})
def link_to_external(name, link, **options)
options[:class] ||= 'external'
options[:class] = "#{options[:class]} external" if options[:class].exclude? 'external'
@ -16,17 +18,17 @@ module Additionals
link_to name, link, options
end
def additionals_list_title(options)
def additionals_list_title(name: nil, issue: nil, user: nil, query: nil)
title = []
if options[:issue]
title << link_to(h("#{options[:issue].subject} ##{options[:issue].id}"),
issue_path(options[:issue]),
class: options[:issue].css_classes)
elsif options[:user]
title << safe_join([avatar(options[:user], size: 50), options[:user].name], ' ')
if issue
title << link_to(h("#{issue.subject} ##{issue.id}"),
issue_path(issue),
class: issue.css_classes)
elsif user
title << safe_join([avatar(user, size: 50), user.name], ' ')
end
title << options[:name] if options[:name]
title << h(options[:query].name) if options[:query] && !options[:query].new_record?
title << name if name
title << h(query.name) if query && !query.new_record?
safe_join title, Additionals::LIST_SEPARATOR
end
@ -53,7 +55,7 @@ module Additionals
def render_issue_macro_link(issue, text, comment_id = nil)
only_path = controller_path.split('_').last != 'mailer'
content = link_to(text, issue_url(issue, only_path: only_path), class: issue.css_classes)
content = link_to text, issue_url(issue, only_path: only_path), class: issue.css_classes
if comment_id.nil?
content
else
@ -76,7 +78,7 @@ module Additionals
comment = 'N/A'
comment_link = comment_id
else
comment_link = link_to(comment_id, issue_url(issue, only_path: only_path, anchor: "note-#{comment_id}"))
comment_link = link_to comment_id, issue_url(issue, only_path: only_path, anchor: "note-#{comment_id}")
end
tag.div class: 'issue-macro box' do
@ -105,14 +107,14 @@ module Additionals
# if more than one projects available, we do not use project url for a new issue
if project_count > 1
if permission == :edit_issues
new_issue_path('issue[assigned_to_id]' => user.id, 'issue[project_id]' => project_id)
new_issue_path 'issue[assigned_to_id]' => user.id, 'issue[project_id]' => project_id
else
new_issue_path('issue[project_id]' => project_id)
new_issue_path 'issue[project_id]' => project_id
end
elsif permission == :edit_issues
new_project_issue_path(project_id, 'issue[assigned_to_id]' => user.id)
new_project_issue_path project_id, 'issue[assigned_to_id]' => user.id
else
new_project_issue_path(project_id)
new_project_issue_path project_id
end
end
@ -125,10 +127,10 @@ module Additionals
return rc
end
uri = URI.parse(url)
uri = URI.parse url
# support issue_id plugin
# see https://www.redmine.org/plugins/issue_id
issue_id_parts = url.split('-')
issue_id_parts = url.split '-'
if uri.scheme.nil? && uri.path[0] != '/' && issue_id_parts.count == 2
rc[:issue_id] = url
else
@ -153,10 +155,10 @@ module Additionals
safe_join s
end
def autocomplete_select_entries(name, type, option_tags, options = {})
def autocomplete_select_entries(name, type, option_tags, **options)
unless option_tags.is_a?(String) || option_tags.blank?
# if option_tags is not an array, it should be an object
option_tags = options_for_select([[option_tags.try(:name), option_tags.try(:id)]], option_tags.try(:id))
option_tags = options_for_select [[option_tags.try(:name), option_tags.try(:id)]], option_tags.try(:id)
end
options[:project] = @project if @project && options[:project].blank?
@ -168,7 +170,7 @@ module Additionals
multiple: options[:multiple],
disabled: options[:disabled])
s << render(layout: false,
partial: 'additionals/select2_ajax_call.js',
partial: 'additionals/select2_ajax_call',
formats: [:js],
locals: { field_id: sanitize_to_id(name),
ajax_url: send("#{type}_path", project_id: options[:project], user_id: options[:user_id]),
@ -186,12 +188,12 @@ module Additionals
classes.join ' '
end
def addtionals_textarea_cols(text, options = {})
[[(options[:min].presence || 8), text.to_s.length / 50].max, (options[:max].presence || 20)].min
def addtionals_textarea_cols(text, min: 8, max: 20)
[[min, text.to_s.length / 50].max, max].min
end
def title_with_fontawesome(title, symbole, wrapper = 'span')
tag.send(wrapper) do
tag.send wrapper do
concat tag.i class: "#{symbole} for-fa-title", 'aria-hidden': 'true'
concat title
end
@ -202,7 +204,7 @@ module Additionals
def additionals_already_loaded(scope, js_name)
locked = "#{js_name}.#{scope}"
@alreaded_loaded = [] if @alreaded_loaded.nil?
return true if @alreaded_loaded.include?(locked)
return true if @alreaded_loaded.include? locked
@alreaded_loaded << locked
false
@ -276,21 +278,19 @@ module Additionals
additionals_include_js 'd3plus-network.full.min'
end
def user_with_avatar(user, options = {})
def user_with_avatar(user, no_link: false, css_class: 'additionals-avatar', size: 14)
return if user.nil?
if user.type == 'Group'
if options[:no_link] || !Redmine::Plugin.installed?('redmine_hrm')
if no_link || !Redmine::Plugin.installed?('redmine_hrm')
user.name
else
link_to_hrm_group user
end
else
options[:size] = 14 if options[:size].nil?
options[:class] = 'additionals-avatar' if options[:class].nil?
s = []
s << avatar(user, options)
s << if options[:no_link]
s << avatar(user, { size: size, class: css_class })
s << if no_link
user.name
else
link_to_user user
@ -305,10 +305,10 @@ module Additionals
l(:label_app_menu) => 'app' }, active)
end
def human_float_number(value, options = {})
def human_float_number(value, precision: 2, separator: '.')
ActionController::Base.helpers.number_with_precision(value,
precision: options[:precision].presence || 2,
separator: options[:separator].presence || '.',
precision: precision,
separator: separator,
strip_insignificant_zeros: true)
end

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
module Additionals
class AdditionalsHookListener < Redmine::Hook::ViewListener
include IssuesHelper

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
module Additionals
module Patches
module AccessControlPatch

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
module Additionals
module Patches
module ApplicationControllerPatch

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
module Additionals
module Patches
module AutoCompletesControllerPatch

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
module Additionals
module Patches
module FormatterMarkdownPatch
@ -8,7 +10,7 @@ module Additionals
# Add a postprocess hook to redcarpet's html formatter
def postprocess(text)
if Additionals.setting?(:legacy_smiley_support)
if Additionals.setting? :legacy_smiley_support
render_inline_smileys(inline_emojify(text))
else
text

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
module Additionals
module Patches
module FormatterTextilePatch

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
module Additionals
module Patches
module FormattingHelperPatch

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
module Additionals
module Patches
module IssuePatch
@ -23,27 +25,22 @@ module Additionals
end
class_methods do
def join_issue_status(options = {})
sql = "JOIN #{IssueStatus.table_name} ON #{IssueStatus.table_name}.id = #{table_name}.status_id"
return sql unless options.key?(:is_closed)
def join_issue_status(is_closed: nil)
sql = +"JOIN #{IssueStatus.table_name} ON #{IssueStatus.table_name}.id = #{table_name}.status_id"
return sql if is_closed.nil?
sql << " AND #{IssueStatus.table_name}.is_closed ="
sql << if options[:is_closed]
" #{connection.quoted_true}"
else
" #{connection.quoted_false}"
end
sql << " AND #{IssueStatus.table_name}.is_closed = #{is_closed ? connection.quoted_true : connection.quoted_false}"
sql
end
end
module InstanceMethods
def sidbar_change_status_allowed_to(user, new_status_id = nil)
statuses = new_statuses_allowed_to(user)
statuses = new_statuses_allowed_to user
if new_status_id.present?
statuses.detect { |s| new_status_id == s.id && !timelog_required?(s.id) }
else
statuses.reject { |s| timelog_required?(s.id) }
statuses.reject { |s| timelog_required? s.id }
end
end
@ -63,7 +60,7 @@ module Additionals
return if Redmine::Plugin.installed?('redmine_automation') && author_id == RedmineAutomation.bot_user_id
add_autowatcher User.current
add_autowatcher(author) if (new_record? || author_id != author_id_was) && author != User.current
add_autowatcher author if (new_record? || author_id != author_id_was) && author != User.current
if !assigned_to_id.nil? && assigned_to_id != User.current.id && (new_record? || assigned_to_id != assigned_to_id_was)
add_autowatcher assigned_to
@ -127,7 +124,7 @@ module Additionals
end
def auto_assigned_to_user
manager_role = Role.builtin.find_by(id: Additionals.setting(:issue_auto_assign_role))
manager_role = Role.builtin.find_by id: Additionals.setting(:issue_auto_assign_role)
groups = autoassign_get_group_list
return groups[manager_role].first.id unless groups.nil? || groups[manager_role].blank?
@ -150,7 +147,7 @@ module Additionals
end
def validate_timelog_required
return true unless timelog_required?(status_id)
return true unless timelog_required? status_id
errors.add :base, :issue_requires_timelog
end

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
module Additionals
module Patches
module IssuePriorityPatch
@ -13,7 +15,7 @@ module Additionals
module InstanceMethods
def css_classes_with_additionals
classes = [css_classes_without_additionals, css_name_based_class]
classes.join(' ')
classes.join ' '
end
# css class based on priority name

View File

@ -1,15 +1,17 @@
# frozen_string_literal: true
module Additionals
module Patches
module PrincipalPatch
extend ActiveSupport::Concern
included do
scope :assignable, -> { active.visible.where(type: %w[User Group]) }
scope :assignable, -> { active.visible.where type: %w[User Group] }
scope :assignable_for_issues, lambda { |*args|
project = args.first
users = assignable
users = users.where.not(type: 'Group') unless Setting.issue_group_assignment?
users = users.where.not type: 'Group' unless Setting.issue_group_assignment?
users = users.joins(members: :roles)
.where(roles: { assignable: true })
.distinct
@ -35,7 +37,7 @@ module Additionals
active
else
# self and members of visible projects
scope = if user.allowed_to?(:show_hidden_roles_in_memberbox, nil, global: true)
scope = if user.allowed_to? :show_hidden_roles_in_memberbox, nil, global: true
active.where("#{table_name}.id = ? OR #{table_name}.id IN (SELECT user_id " \
"FROM #{Member.table_name} WHERE project_id IN (?))",
user.id, user.visible_project_ids)

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
module Additionals
module Patches
module ProjectPatch
@ -52,12 +54,12 @@ module Additionals
end
def visible_principals
query = ::Query.new(project: self, name: '_')
query = ::Query.new project: self, name: '_'
query&.principals
end
def visible_users
query = ::Query.new(project: self, name: '_')
query = ::Query.new project: self, name: '_'
query&.users
end

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
require_dependency 'projects_controller'
module Additionals
@ -27,7 +29,7 @@ module Additionals
def find_dashboard
if params[:dashboard_id].present?
begin
@dashboard = Dashboard.project_only.find(params[:dashboard_id])
@dashboard = Dashboard.project_only.find params[:dashboard_id]
raise ::Unauthorized unless @dashboard.visible?
raise ::Unauthorized unless @dashboard.project.nil? || @dashboard.project == @project
rescue ActiveRecord::RecordNotFound

View File

@ -0,0 +1,14 @@
# frozen_string_literal: true
module Additionals
module Patches
module QueriesHelperPatch
extend ActiveSupport::Concern
included do
def additional_csv_separator
l(:general_csv_separator) == ',' ? ';' : ','
end
end
end
end
end

Some files were not shown because too many files have changed in this diff Show More