Working on ruby 2.7 updates and warning fixes
This commit is contained in:
parent
a48ee5b4a7
commit
07c306fa66
3
.github/workflows/linters.yml
vendored
3
.github/workflows/linters.yml
vendored
@ -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
2
.gitignore
vendored
@ -6,7 +6,9 @@ tmp/
|
||||
Gemfile.lock
|
||||
.project
|
||||
.vscode
|
||||
.bundle
|
||||
.settings/
|
||||
docs/_build
|
||||
docs/_static
|
||||
docs/_templates
|
||||
.enable_dev
|
||||
|
43
.rubocop.yml
43
.rubocop.yml
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -1,6 +1,11 @@
|
||||
Changelog
|
||||
=========
|
||||
|
||||
3.0.3
|
||||
+++++
|
||||
|
||||
- Ruby 2.7 warnings fixed
|
||||
|
||||
3.0.2
|
||||
+++++
|
||||
|
||||
|
18
Gemfile
18
Gemfile
@ -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
|
||||
|
2
Rakefile
2
Rakefile
@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'bundler/gem_tasks'
|
||||
require 'rake/testtask'
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class AdditionalsMacrosController < ApplicationController
|
||||
before_action :require_login
|
||||
|
||||
|
@ -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
|
||||
|
@ -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?
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module AdditionalsCustomFieldsHelper
|
||||
def custom_fields_with_full_with_layout
|
||||
['IssueCustomField']
|
||||
|
@ -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
|
||||
|
@ -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')
|
||||
|
@ -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
|
||||
|
@ -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 }],
|
||||
|
@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module AdditionalsProjectsHelper
|
||||
def project_overview_name(_project, dashboard = nil)
|
||||
name = [l(:label_overview)]
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class AdditionalsJob < ActiveJob::Base
|
||||
# Automatically retry jobs that encountered a deadlock
|
||||
# retry_on ActiveRecord::Deadlocked
|
||||
|
@ -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
|
||||
|
@ -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?
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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])
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -1,5 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class DashboardContentProject < DashboardContent
|
||||
TYPE_NAME = 'ProjectDashboard'.freeze
|
||||
TYPE_NAME = 'ProjectDashboard'
|
||||
|
||||
def block_definitions
|
||||
blocks = super
|
||||
|
@ -1,5 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class DashboardContentWelcome < DashboardContent
|
||||
TYPE_NAME = 'WelcomeDashboard'.freeze
|
||||
TYPE_NAME = 'WelcomeDashboard'
|
||||
|
||||
def block_definitions
|
||||
blocks = super
|
||||
|
@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class DashboardRole < ActiveRecord::Base
|
||||
include Redmine::SafeAttributes
|
||||
|
||||
|
@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
Deface::Override.new virtual_path: 'admin/info',
|
||||
name: 'add-system_info',
|
||||
insert_after: 'table.list',
|
||||
|
@ -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',
|
||||
|
@ -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"\')',
|
||||
|
@ -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)")',
|
||||
|
@ -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\'")',
|
||||
|
@ -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)")',
|
||||
|
@ -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?")',
|
||||
|
@ -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',
|
||||
|
@ -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',
|
||||
|
@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
Deface::Override.new virtual_path: 'wiki/edit',
|
||||
name: 'wiki-edit-bottom',
|
||||
insert_before: 'fieldset',
|
||||
|
@ -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',
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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] },
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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]
|
||||
|
@ -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|
|
||||
|
@ -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 },
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 }
|
||||
|
||||
|
@ -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'
|
||||
|
@ -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() {
|
||||
|
@ -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
|
||||
|
||||
|
@ -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),
|
||||
|
@ -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
|
||||
|
||||
|
@ -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'
|
||||
|
@ -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|
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class CreateDashboards < ActiveRecord::Migration[5.2]
|
||||
def change
|
||||
create_table :dashboards do |t|
|
||||
|
@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class CreateDashboardRoles < ActiveRecord::Migration[5.2]
|
||||
def change
|
||||
create_table :dashboard_roles do |t|
|
||||
|
@ -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,
|
||||
|
@ -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.
|
||||
|
6
init.rb
6
init.rb
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Additionals
|
||||
module EntityMethods
|
||||
def assignable_users(prj = nil)
|
||||
|
@ -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}:",
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Additionals
|
||||
class AdditionalsHookListener < Redmine::Hook::ViewListener
|
||||
include IssuesHelper
|
||||
|
@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Additionals
|
||||
module Patches
|
||||
module AccessControlPatch
|
||||
|
@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Additionals
|
||||
module Patches
|
||||
module ApplicationControllerPatch
|
||||
|
@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Additionals
|
||||
module Patches
|
||||
module AutoCompletesControllerPatch
|
||||
|
@ -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
|
||||
|
@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Additionals
|
||||
module Patches
|
||||
module FormatterTextilePatch
|
||||
|
@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Additionals
|
||||
module Patches
|
||||
module FormattingHelperPatch
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
14
lib/additionals/patches/queries_helper_patch.rb
Normal file
14
lib/additionals/patches/queries_helper_patch.rb
Normal 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
Loading…
Reference in New Issue
Block a user