Merge branch 'master' into stable
This commit is contained in:
commit
0610176052
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,17 @@
|
||||
Changelog
|
||||
=========
|
||||
|
||||
3.0.3
|
||||
+++++
|
||||
|
||||
- Ruby 2.7 warnings fixed
|
||||
- Mermaid 8.11.5 support
|
||||
- D3 7.0.1 support
|
||||
- Remove issue macro, which is now supported by Redmine core
|
||||
- new ticket message can be overwritten for projects
|
||||
- fixed scope of public project dashboards for all projects
|
||||
- FontAwesome 5.15.4 support
|
||||
|
||||
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|
|
||||
@ -17,14 +19,9 @@ Gem::Specification.new do |spec|
|
||||
spec.require_paths = ['lib']
|
||||
spec.required_ruby_version = '>= 2.5'
|
||||
|
||||
spec.add_runtime_dependency 'deface', '1.5.3'
|
||||
spec.add_runtime_dependency 'deface', '1.8.1'
|
||||
spec.add_runtime_dependency 'gemoji', '~> 3.0.0'
|
||||
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
|
||||
|
65
app/controllers/additionals_journals_controller.rb
Normal file
65
app/controllers/additionals_journals_controller.rb
Normal file
@ -0,0 +1,65 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# This file is a part of redmine_db,
|
||||
# a Redmine plugin to manage custom database entries.
|
||||
#
|
||||
# Copyright (c) 2016-2021 AlphaNodes GmbH
|
||||
# https://alphanodes.com
|
||||
|
||||
class AdditionalsJournalsController < ApplicationController
|
||||
before_action :find_journal, only: %i[edit update diff]
|
||||
before_action :authorize, only: %i[edit update]
|
||||
|
||||
helper :custom_fields
|
||||
helper :journals
|
||||
helper :additionals_journals
|
||||
|
||||
def diff
|
||||
@entry = @journal.journalized
|
||||
@detail =
|
||||
if params[:detail_id].present?
|
||||
@journal.details.find_by id: params[:detail_id]
|
||||
else
|
||||
@journal.details.detect { |d| d.property == 'attr' && d.prop_key == 'description' }
|
||||
end
|
||||
unless @entry && @detail
|
||||
render_404
|
||||
return false
|
||||
end
|
||||
|
||||
raise ::Unauthorized if @detail.property == 'cf' && !@detail.custom_field&.visible_by?(@entry.project, User.current)
|
||||
|
||||
@diff = Redmine::Helpers::Diff.new @detail.value, @detail.old_value
|
||||
end
|
||||
|
||||
def edit
|
||||
return render_403 unless @journal.editable_by? User.current
|
||||
|
||||
respond_to do |format|
|
||||
# TODO: implement non-JS journal update
|
||||
format.js
|
||||
end
|
||||
end
|
||||
|
||||
def update
|
||||
return render_403 unless @journal.editable_by? User.current
|
||||
|
||||
@journal.safe_attributes = params[:journal]
|
||||
@journal.save
|
||||
@journal.destroy if @journal.details.empty? && @journal.notes.blank?
|
||||
respond_to do |format|
|
||||
format.html { redirect_after_update }
|
||||
format.js
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def redirect_after_update
|
||||
raise 'overwrite it'
|
||||
end
|
||||
|
||||
def find_journal
|
||||
raise 'overwrite it'
|
||||
end
|
||||
end
|
@ -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
|
||||
|
||||
@ -49,7 +51,7 @@ class DashboardsController < ApplicationController
|
||||
|
||||
def show
|
||||
respond_to do |format|
|
||||
format.html { head 406 }
|
||||
format.html { head :not_acceptable }
|
||||
format.js if request.xhr?
|
||||
format.api
|
||||
end
|
||||
@ -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
|
||||
@ -183,7 +185,7 @@ class DashboardsController < ApplicationController
|
||||
def order_blocks
|
||||
@dashboard.order_blocks params[:group], params[:blocks]
|
||||
@dashboard.save
|
||||
head 200
|
||||
head :ok
|
||||
end
|
||||
|
||||
private
|
||||
@ -201,11 +203,15 @@ 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?
|
||||
@dashboard.content_project = find_project_by_project_id
|
||||
@dashboard.content_project = if params[:dashboard].present? && params[:dashboard][:content_project_id].present?
|
||||
find_project params[:dashboard][:content_project_id]
|
||||
else
|
||||
find_project_by_project_id
|
||||
end
|
||||
else
|
||||
@project = @dashboard.project
|
||||
end
|
||||
|
@ -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
|
||||
@ -122,6 +124,33 @@ module AdditionalsJournalsHelper
|
||||
end
|
||||
# rubocop: enable Rails/OutputSafety
|
||||
|
||||
def render_email_attributes(entry, html: false)
|
||||
items = send "email_#{entry.class.name.underscore}_attributes", entry, html
|
||||
if html
|
||||
tag.ul class: 'details' do
|
||||
items.map { |s| concat tag.li(s) }.join("\n")
|
||||
end
|
||||
else
|
||||
items.map { |s| "* #{s}" }.join("\n")
|
||||
end
|
||||
end
|
||||
|
||||
def email_custom_field_values_attributes(entry, html)
|
||||
items = []
|
||||
entry.custom_field_values.each do |value|
|
||||
cf_value = show_value value, false
|
||||
next if cf_value.blank?
|
||||
|
||||
items << if html
|
||||
tag.strong("#{value.custom_field.name}: ") + cf_value
|
||||
else
|
||||
"#{value.custom_field.name}: #{cf_value}"
|
||||
end
|
||||
end
|
||||
|
||||
items
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def entity_show_detail_prop(detail, options)
|
||||
@ -138,20 +167,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'
|
||||
@ -5,7 +7,7 @@ module AdditionalsMenuHelper
|
||||
if Additionals.setting? :remove_mypage
|
||||
Redmine::MenuManager.map(:top_menu).delete(:my_page) if Redmine::MenuManager.map(:top_menu).exists?(:my_page)
|
||||
else
|
||||
handle_top_menu_item(:my_page, { url: my_page_path, after: :home, if: proc { User.current.logged? } })
|
||||
handle_top_menu_item(:my_page, url: my_page_path, after: :home, onlyif: proc { User.current.logged? })
|
||||
end
|
||||
|
||||
if Additionals.setting? :remove_help
|
||||
@ -18,46 +20,47 @@ 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, url:, with_submenu: false, onlyif: nil,
|
||||
name: nil, parent: nil, title: nil, symbol: nil, before: nil, after: nil, last: false)
|
||||
Redmine::MenuManager.map(:top_menu).delete(menu_name.to_sym) if Redmine::MenuManager.map(:top_menu).exists?(menu_name.to_sym)
|
||||
|
||||
html_options = {}
|
||||
|
||||
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?
|
||||
css_classes << 'external' if url.include? '://'
|
||||
html_options[:class] = css_classes.join ' ' if css_classes.present?
|
||||
|
||||
html_options[:title] = item[:title] if item[:title].present?
|
||||
html_options[:title] = title if title.present?
|
||||
|
||||
menu_options = { parent: item[:parent].present? ? item[:parent].to_sym : nil,
|
||||
menu_options = { parent: parent.present? ? parent.to_sym : nil,
|
||||
html: html_options }
|
||||
|
||||
menu_options[:if] = menu_options[:if] if menu_options[:if].present?
|
||||
menu_options[:if] = onlyif if onlyif.present?
|
||||
|
||||
menu_options[:caption] = if item[:symbol].present? && item[:name].present?
|
||||
font_awesome_icon(item[:symbol], post_text: item[:name])
|
||||
elsif item[:symbol].present?
|
||||
font_awesome_icon(item[:symbol])
|
||||
elsif item[:name].present?
|
||||
item[:name].to_s
|
||||
menu_options[:caption] = if symbol.present? && name.present?
|
||||
font_awesome_icon symbol, post_text: name
|
||||
elsif symbol.present?
|
||||
font_awesome_icon symbol
|
||||
elsif name.present?
|
||||
name.to_s
|
||||
end
|
||||
|
||||
if item[:last].present? && item[:last]
|
||||
if last
|
||||
menu_options[:last] = true
|
||||
elsif item[:before].present?
|
||||
menu_options[:before] = item[:before]
|
||||
elsif item[:after].present?
|
||||
menu_options[:after] = item[:after]
|
||||
elsif before.present?
|
||||
menu_options[:before] = before
|
||||
elsif after.present?
|
||||
menu_options[:after] = after
|
||||
else
|
||||
menu_options[:before] = :help
|
||||
end
|
||||
|
||||
Redmine::MenuManager.map(:top_menu).push menu_name, item[:url], menu_options
|
||||
Redmine::MenuManager.map(:top_menu).push menu_name, url, **menu_options
|
||||
end
|
||||
|
||||
def render_custom_top_menu_item
|
||||
@ -99,7 +102,8 @@ module AdditionalsMenuHelper
|
||||
|
||||
def additionals_custom_top_menu_item(item, user_roles)
|
||||
show_entry = false
|
||||
item[:roles].each do |role|
|
||||
roles = item.delete :roles
|
||||
roles.each do |role|
|
||||
if user_roles.empty? && role.to_i == Role::BUILTIN_ANONYMOUS ||
|
||||
# if user is logged in and non_member is active in item, always show it
|
||||
User.current.logged? && role.to_i == Role::BUILTIN_NON_MEMBER
|
||||
@ -113,13 +117,14 @@ module AdditionalsMenuHelper
|
||||
break
|
||||
end
|
||||
end
|
||||
break if show_entry == true
|
||||
break if show_entry
|
||||
end
|
||||
|
||||
menu_name = item.delete :menu_name
|
||||
if show_entry
|
||||
handle_top_menu_item item[:menu_name], item
|
||||
elsif Redmine::MenuManager.map(:top_menu).exists?(item[:menu_name])
|
||||
Redmine::MenuManager.map(:top_menu).delete(item[:menu_name])
|
||||
handle_top_menu_item menu_name, item
|
||||
elsif Redmine::MenuManager.map(:top_menu).exists?(menu_name)
|
||||
Redmine::MenuManager.map(:top_menu).delete(menu_name)
|
||||
end
|
||||
end
|
||||
|
||||
@ -127,8 +132,8 @@ module AdditionalsMenuHelper
|
||||
user_items = [{ title: 'Redmine Guide', url: Redmine::Info.help_url },
|
||||
{ title: "Redmine #{l :label_macro_plural}", url: additionals_macros_path }]
|
||||
|
||||
admin_items = [{ title: 'Additionals',
|
||||
url: 'https://additionals.readthedocs.io/en/latest/manual/', manual: true },
|
||||
admin_items = [{ title: "Additionals #{l :label_help_manual}",
|
||||
url: 'https://additionals.readthedocs.io/en/latest/manual/' },
|
||||
{ title: 'Redmine Changelog',
|
||||
url: "https://www.redmine.org/projects/redmine/wiki/Changelog_#{Redmine::VERSION::MAJOR}_#{Redmine::VERSION::MINOR}" },
|
||||
{ title: 'Redmine Upgrade',
|
||||
@ -144,13 +149,13 @@ module AdditionalsMenuHelper
|
||||
begin
|
||||
plugin_item_base = plugin.id.to_s.camelize.constantize
|
||||
rescue LoadError
|
||||
Rails.logger.debug "Ignore plugin #{plugin.id} for help integration"
|
||||
Rails.logger.debug { "Ignore plugin #{plugin.id} for help integration" }
|
||||
rescue StandardError => e
|
||||
raise e unless e.class.to_s == 'NameError'
|
||||
end
|
||||
|
||||
plugin_item = plugin_item_base.try(:additionals_help_items) unless plugin_item_base.nil?
|
||||
plugin_item = additionals_help_items_fallbacks(plugin.id) if plugin_item.nil?
|
||||
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 +191,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 +205,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)]
|
||||
@ -5,4 +7,10 @@ module AdditionalsProjectsHelper
|
||||
|
||||
safe_join name, Additionals::LIST_SEPARATOR
|
||||
end
|
||||
|
||||
def render_api_includes(project, api)
|
||||
super
|
||||
api.enable_new_ticket_message project.enable_new_ticket_message
|
||||
api.new_ticket_message project.new_ticket_message
|
||||
end
|
||||
end
|
||||
|
@ -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
|
||||
@ -268,11 +270,4 @@ module AdditionalsQueriesHelper
|
||||
|
||||
tags
|
||||
end
|
||||
|
||||
def render_query_group_view(query, locals = {})
|
||||
return if locals[:group_name].blank?
|
||||
|
||||
render partial: 'queries/additionals_group_view',
|
||||
locals: { query: query }.merge(locals)
|
||||
end
|
||||
end
|
||||
|
@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module AdditionalsRoutesHelper
|
||||
def _dashboards_path(project, *args)
|
||||
if project
|
||||
@ -71,12 +73,18 @@ 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
|
||||
|
||||
case dashboard.dashboard_type
|
||||
when DashboardContentProject::TYPE_NAME
|
||||
project_path project, **options
|
||||
when DashboardContentWelcome::TYPE_NAME
|
||||
home_path(**options)
|
||||
else
|
||||
home_path options
|
||||
dashboard_type_name = dashboard.dashboard_type[0..-10]
|
||||
route_helper = "DashboardContent#{dashboard_type_name}::ROUTE_HELPER".constantize
|
||||
send route_helper, **options
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -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,53 +38,61 @@ 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_timefield(name, **options)
|
||||
additionals_settings_input_field :time_field_tag, name, **options
|
||||
end
|
||||
|
||||
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]
|
||||
value = if options.key? :value
|
||||
options.delete :value
|
||||
elsif @settings.present?
|
||||
@settings[name]
|
||||
end
|
||||
|
||||
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
|
||||
options.delete :value
|
||||
elsif @settings.present?
|
||||
@settings[name]
|
||||
end
|
||||
@ -91,6 +101,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))),
|
||||
@ -30,17 +32,13 @@ module DashboardsHelper
|
||||
safe_join classes, ' '
|
||||
end
|
||||
|
||||
def sidebar_dashboards(dashboard, project = nil, user = nil)
|
||||
user ||= User.current
|
||||
scope = Dashboard.visible.includes([:author])
|
||||
def sidebar_dashboards(dashboard, project = nil)
|
||||
scope = Dashboard.visible.includes [:author]
|
||||
|
||||
scope = if project.present?
|
||||
scope = scope.project_only
|
||||
scope.where(project_id: project.id)
|
||||
.or(scope.where(system_default: true)
|
||||
.where(project_id: nil))
|
||||
.or(scope.where(author_id: user.id)
|
||||
.where(project_id: nil))
|
||||
.or(scope.where(project_id: nil))
|
||||
else
|
||||
scope.where dashboard_type: dashboard.dashboard_type
|
||||
end
|
||||
@ -111,7 +109,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 +117,7 @@ module DashboardsHelper
|
||||
active_dashboard.id
|
||||
end
|
||||
|
||||
css = 'dashboard'
|
||||
css = +'dashboard'
|
||||
css << ' selected' if selected
|
||||
li_class = nil
|
||||
|
||||
@ -143,14 +141,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 +169,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 +250,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 +275,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 +287,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 +320,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 +355,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 +377,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 +389,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 +428,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 +439,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
|
||||
|
||||
@ -177,16 +161,12 @@ module AdditionalsQuery
|
||||
|
||||
db_table = Watcher.table_name
|
||||
"#{queried_table_name}.id #{operator == '=' ? 'IN' : 'NOT IN'}" \
|
||||
" (SELECT #{db_table}.watchable_id FROM #{db_table} WHERE #{db_table}.watchable_type='#{watchable_type}' AND" \
|
||||
" #{sql_for_field field, '=', value, db_table, 'user_id'})"
|
||||
end
|
||||
|
||||
def sql_for_tags_field(field, _operator, value)
|
||||
AdditionalTags.sql_for_tags_field queried_class, operator_for(field), value
|
||||
" (SELECT #{db_table}.watchable_id FROM #{db_table} WHERE #{db_table}.watchable_type='#{watchable_type}' AND" \
|
||||
" #{sql_for_field field, '=', value, db_table, 'user_id'})"
|
||||
end
|
||||
|
||||
def sql_for_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,9 +49,11 @@ 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_validation :strip_whitespace
|
||||
|
||||
before_save :dashboard_type_check, :visibility_check, :set_options_hash, :clear_unused_block_settings
|
||||
|
||||
before_destroy :check_destroy_system_default
|
||||
@ -73,10 +77,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 +90,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 +105,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)))
|
||||
|
||||
@ -109,14 +113,14 @@ class Dashboard < ActiveRecord::Base
|
||||
scope.where.not(visibility: VISIBILITY_PRIVATE).or(scope.where(author_id: user.id))
|
||||
elsif user.memberships.any?
|
||||
scope.where("#{table_name}.visibility = ?" \
|
||||
" OR (#{table_name}.visibility = ? AND #{table_name}.id IN (" \
|
||||
"SELECT DISTINCT d.id FROM #{table_name} d" \
|
||||
" INNER JOIN #{table_name_prefix}dashboard_roles#{table_name_suffix} dr ON dr.dashboard_id = d.id" \
|
||||
" INNER JOIN #{MemberRole.table_name} mr ON mr.role_id = dr.role_id" \
|
||||
" INNER JOIN #{Member.table_name} m ON m.id = mr.member_id AND m.user_id = ?" \
|
||||
" INNER JOIN #{Project.table_name} p ON p.id = m.project_id AND p.status <> ?" \
|
||||
' WHERE d.project_id IS NULL OR d.project_id = m.project_id))' \
|
||||
" OR #{table_name}.author_id = ?",
|
||||
" OR (#{table_name}.visibility = ? AND #{table_name}.id IN (" \
|
||||
"SELECT DISTINCT d.id FROM #{table_name} d" \
|
||||
" INNER JOIN #{table_name_prefix}dashboard_roles#{table_name_suffix} dr ON dr.dashboard_id = d.id" \
|
||||
" INNER JOIN #{MemberRole.table_name} mr ON mr.role_id = dr.role_id" \
|
||||
" INNER JOIN #{Member.table_name} m ON m.id = mr.member_id AND m.user_id = ?" \
|
||||
" INNER JOIN #{Project.table_name} p ON p.id = m.project_id AND p.status <> ?" \
|
||||
' WHERE d.project_id IS NULL OR d.project_id = m.project_id))' \
|
||||
" OR #{table_name}.author_id = ?",
|
||||
VISIBILITY_PUBLIC,
|
||||
VISIBILITY_ROLES,
|
||||
user.id,
|
||||
@ -152,7 +156,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 +240,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 +265,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 +278,7 @@ class Dashboard < ActiveRecord::Base
|
||||
end
|
||||
|
||||
def destroyable?
|
||||
@destroyable ||= destroyable_by?(User.current)
|
||||
@destroyable ||= destroyable_by? User.current
|
||||
end
|
||||
|
||||
def to_s
|
||||
@ -285,7 +289,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 +297,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 +330,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}"
|
||||
@ -343,9 +347,13 @@ class Dashboard < ActiveRecord::Base
|
||||
|
||||
private
|
||||
|
||||
def strip_whitespace
|
||||
name&.strip!
|
||||
end
|
||||
|
||||
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 +399,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 +417,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
|
||||
|
||||
|
8
app/models/query_relations_column.rb
Normal file
8
app/models/query_relations_column.rb
Normal file
@ -0,0 +1,8 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class QueryRelationsColumn < QueryColumn
|
||||
# NOTE: used for CSV and PDF export
|
||||
def value_object(object)
|
||||
(object.send name).map(&:name).join "#{Query.additional_csv_separator} "
|
||||
end
|
||||
end
|
@ -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,5 +1,9 @@
|
||||
Deface::Override.new virtual_path: 'issues/_list',
|
||||
name: 'list-issue-back-url',
|
||||
replace: 'erb[loud]:contains("hidden_field_tag \'back_url\'")',
|
||||
original: '6652d55078bb57ac4614e456b01f8a203b8096ec',
|
||||
text: '<%= query_list_back_url_tag @project %>'
|
||||
# frozen_string_literal: true
|
||||
|
||||
unless Redmine::Plugin.installed? 'redmine_reporting'
|
||||
Deface::Override.new virtual_path: 'issues/_list',
|
||||
name: 'list-issue-back-url',
|
||||
replace: 'erb[loud]:contains("hidden_field_tag \'back_url\'")',
|
||||
original: '6652d55078bb57ac4614e456b01f8a203b8096ec',
|
||||
text: '<%= query_list_back_url_tag @project %>'
|
||||
end
|
||||
|
@ -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)")',
|
||||
|
15
app/overrides/projects/settings_issues.rb
Normal file
15
app/overrides/projects/settings_issues.rb
Normal file
@ -0,0 +1,15 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
if Redmine::VERSION.to_s >= '4.3' || Redmine::VERSION.to_s >= '4.2' && Redmine::VERSION.to_s.include?('devel')
|
||||
Deface::Override.new virtual_path: 'projects/settings/_issues',
|
||||
name: 'add-project-issue-settings',
|
||||
insert_before: 'div.box.tabular',
|
||||
original: 'f14b1e325de2e0de0e81443716705547a6c8471f',
|
||||
text: '<%= call_hook :view_projects_issue_settings, f: f, project: @project %>'
|
||||
else
|
||||
Deface::Override.new virtual_path: 'projects/settings/_issues',
|
||||
name: 'add-project-issue-settings',
|
||||
insert_before: 'div.box.tabular',
|
||||
original: '468bd73ed808b51fc5589f6c9c23e93a5b7b787a',
|
||||
text: '<%= call_hook :view_projects_issue_settings, f: f, project: @project %>'
|
||||
end
|
@ -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,35 +1,37 @@
|
||||
div id="#{export_format}-export-options" style="display: none"
|
||||
h3.title = l(:label_export_options, export_format: export_format.upcase)
|
||||
div id="#{export_format}-export-options" style="display: none;"
|
||||
h3.title = l :label_export_options, export_format: export_format.upcase
|
||||
|
||||
= form_tag(url, method: :get, id: "#{export_format}-export-form") do
|
||||
= query_as_hidden_field_tags @query
|
||||
= 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)
|
||||
- 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')
|
||||
p
|
||||
label
|
||||
= check_box_tag 'c[]', 'description', @query.has_column?(:description)
|
||||
= l(:field_description)
|
||||
- if defined?(with_last_notes) && with_last_notes
|
||||
- if query.available_block_columns.any?
|
||||
fieldset#csv-export-block-columns
|
||||
legend = toggle_checkboxes_link '#csv-export-block-columns input[type=checkbox]'
|
||||
- query.available_block_columns.each do |column|
|
||||
label
|
||||
= check_box_tag 'c[]', 'last_notes', @query.has_column?(:last_notes)
|
||||
= l(:label_last_notes)
|
||||
= check_box_tag 'c[]', column.name, query.has_column?(column), id: nil
|
||||
= column.caption
|
||||
|
||||
= export_csv_encoding_select_tag
|
||||
|
||||
- if @issue_count && @issue_count > Setting.issues_export_limit.to_i || \
|
||||
@query_count && @query_count > Setting.issues_export_limit.to_i
|
||||
p.icon.icon-warning
|
||||
= @issue_count ? l(:setting_issues_export_limit) : l(:setting_export_limit)
|
||||
' :
|
||||
= Setting.issues_export_limit.to_i
|
||||
|
||||
p.buttons
|
||||
= submit_tag l(:button_export),
|
||||
name: nil,
|
||||
|
@ -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
|
||||
|
@ -12,6 +12,7 @@
|
||||
= hidden_field_tag 'dashboard[dashboard_type]', @dashboard.dashboard_type if @dashboard.new_record?
|
||||
- if @project && @allowed_projects.present? && @allowed_projects.count > 1
|
||||
p
|
||||
= hidden_field_tag 'dashboard[content_project_id]', @project&.id
|
||||
= f.select :project_id,
|
||||
project_tree_options_for_select(@allowed_projects,
|
||||
selected: @dashboard.project,
|
||||
@ -19,7 +20,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 +28,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
|
||||
|
@ -1,3 +1,3 @@
|
||||
- if @issue.new_ticket_message.present?
|
||||
.nodata.nodata-left
|
||||
.new-ticket-message.nodata.nodata-left
|
||||
= textilizable @issue, :new_ticket_message, inline_attachments: false
|
||||
|
45
app/views/projects/_additionals_settings_issues.html.slim
Normal file
45
app/views/projects/_additionals_settings_issues.html.slim
Normal file
@ -0,0 +1,45 @@
|
||||
fieldset.box.tabular
|
||||
legend
|
||||
= l :label_new_ticket_message
|
||||
|
||||
- if User.current.admin?
|
||||
.contextual
|
||||
= link_to l(:label_administration), plugin_settings_path(id: 'additionals', tab: 'rules'), class: 'icon icon-settings'
|
||||
|
||||
p
|
||||
= f.select :enable_new_ticket_message,
|
||||
options_for_select({ l(:label_system_setting) => '1',
|
||||
l(:label_disabled) => '0',
|
||||
l(:label_project_setting) => '2' }, project.enable_new_ticket_message.to_s)
|
||||
p#project-ticket-message style="#{'display: none' if project.enable_new_ticket_message != 2}"
|
||||
= f.text_area :new_ticket_message,
|
||||
rows: addtionals_textarea_cols(project.new_ticket_message),
|
||||
label: l(:label_additionals_message)
|
||||
|
||||
p#system-ticket-message style="#{'display: none' if project.enable_new_ticket_message != 1}"
|
||||
= additionals_settings_textarea :new_ticket_message_system,
|
||||
value: Additionals.setting(:new_ticket_message).to_s,
|
||||
label: l(:label_additionals_message),
|
||||
for_project: true,
|
||||
disabled: true
|
||||
|
||||
javascript:
|
||||
$(function() {
|
||||
$("#project_enable_new_ticket_message").change(function () {
|
||||
if ( this.value == '1')
|
||||
{
|
||||
$("#system-ticket-message").show();
|
||||
$("#project-ticket-message").hide();
|
||||
}
|
||||
else if ( this.value == '2')
|
||||
{
|
||||
$("#system-ticket-message").hide();
|
||||
$("#project-ticket-message").show();
|
||||
}
|
||||
else
|
||||
{
|
||||
$("#system-ticket-message").hide();
|
||||
$("#project-ticket-message").hide();
|
||||
}
|
||||
});
|
||||
});
|
@ -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 }
|
||||
|
||||
|
@ -1,3 +0,0 @@
|
||||
- if @query.description?
|
||||
.query-description
|
||||
= textilizable @query, :description
|
@ -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
|
||||
|
||||
|
@ -16,7 +16,8 @@ jsToolBar.prototype.elements.macros = {
|
||||
jsToolBar.prototype.macroMenu = function(fn){
|
||||
var menu = $('<ul style="position:absolute;"></ul>');
|
||||
for (var i = 0; i < this.macroList.length; i++) {
|
||||
$('<li></li>').text(this.macroList[i]).appendTo(menu).mousedown(function(){
|
||||
var macroItem = $('<div></div>').text(this.macroList[i]);
|
||||
$('<li></li>').html(macroItem).appendTo(menu).mousedown(function(){
|
||||
fn($(this).text());
|
||||
});
|
||||
}
|
||||
|
4
assets/javascripts/d3.min.js
vendored
4
assets/javascripts/d3.min.js
vendored
File diff suppressed because one or more lines are too long
12
assets/javascripts/mermaid.min.js
vendored
12
assets/javascripts/mermaid.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -9,8 +9,13 @@ $(function() {
|
||||
} else {
|
||||
var mermaidThemeVariables = { 'fontSize': '12px' };
|
||||
}
|
||||
|
||||
mermaid.initialize({
|
||||
startOnLoad: true,
|
||||
maxTextSize: 500000,
|
||||
flowchart:{
|
||||
useMaxWidth: false
|
||||
},
|
||||
theme: mermaidTheme,
|
||||
themeVariables: mermaidThemeVariables });
|
||||
});
|
||||
|
@ -377,6 +377,24 @@ select.dashboard-block-select {
|
||||
div.drdn-items a.disabled,
|
||||
div.drdn-items a.disabled:hover { color: #aaa; }
|
||||
|
||||
table.entity-list td.block_column {
|
||||
color: #777;
|
||||
font-size: 90%;
|
||||
padding: 4px 4px 4px 24px;
|
||||
text-align: left;
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
table.entity-list td.block_column span {
|
||||
font-weight: bold;
|
||||
display: block;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
table.entity-list td.block_column pre {
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 899px) {
|
||||
.gototop { display: none; }
|
||||
|
||||
|
4
assets/stylesheets/fontawesome-all.min.css
vendored
4
assets/stylesheets/fontawesome-all.min.css
vendored
File diff suppressed because one or more lines are too long
Binary file not shown.
@ -2,11 +2,11 @@
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1">
|
||||
<metadata>
|
||||
Created by FontForge 20201107 at Tue Mar 16 10:15:04 2021
|
||||
Created by FontForge 20201107 at Wed Aug 4 12:25:29 2021
|
||||
By Robert Madole
|
||||
Copyright (c) Font Awesome
|
||||
</metadata>
|
||||
<!-- Font Awesome Free 5.15.3 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><defs>
|
||||
<!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><defs>
|
||||
<font id="FontAwesome5Brands-Regular" horiz-adv-x="448" >
|
||||
<font-face
|
||||
font-family="Font Awesome 5 Brands Regular"
|
||||
@ -957,14 +957,6 @@ l19.2002 -19.2002l128 128l-128 128l-51.5 -51.5l77.1006 -76.5l-25.6006 -25.5996l-
|
||||
<glyph glyph-name="gg-circle" unicode="" horiz-adv-x="512"
|
||||
d="M257 440c137 0 248 -111 248 -248s-111 -248 -248 -248s-248 111 -248 248s111 248 248 248zM207.5 65.2002l75 75.2002l-77.2002 77.1992l-24.3994 -24.3994l53.0996 -52.9004l-26.5996 -26.5996l-77.2002 77.2002l77.2002 77.1992l11.0996 -11.0996l24.2002 24.2002
|
||||
l-35.2002 35.3994l-125.7 -125.699zM306.5 67.4004l125.7 125.6l-125.7 125.7l-75 -75l77.2002 -77.2002l24.3994 24.4004l-53.0996 52.8994l26.5 26.5l77.2002 -77.2002l-77.2002 -77.1992l-11.0996 11.0996l-24.1006 -24.4004z" />
|
||||
<glyph glyph-name="tripadvisor" unicode="" horiz-adv-x="576"
|
||||
d="M528.91 269.18c28.8652 -26.2666 46.9404 -64.1113 46.9404 -106.176c0 -0.0615234 -0.000976562 -0.12207 -0.000976562 -0.183594h0.0302734c-0.00195312 -79.4414 -64.5479 -143.928 -143.989 -143.928c-37.7363 0 -72.0996 14.5527 -97.7803 38.3477
|
||||
l-46.1104 -50.1699l-46.1396 50.1992c-25.6064 -23.4443 -59.8145 -37.7793 -97.2334 -37.7793c-79.4268 0 -143.911 64.4844 -143.911 143.911c0 41.8076 17.8662 79.4756 46.374 105.778l-47.0898 51.2402h104.66c52.2266 35.5498 115.938 56.3369 183.822 56.3369
|
||||
s130.95 -20.7871 183.178 -56.3369h104.34zM144.06 65.4297c53.751 0 97.3906 43.6396 97.3906 97.3906s-43.6396 97.3896 -97.3906 97.3896s-97.3896 -43.6387 -97.3896 -97.3896s43.6387 -97.3906 97.3896 -97.3906zM288 165.63c0 64.0801 46.6104 119.07 108.08 142.59
|
||||
c-33.2285 13.8467 -70.0527 21.4941 -108.272 21.4941c-38.2207 0 -74.6689 -7.64746 -107.897 -21.4941c61.4697 -23.5098 108.09 -78.5 108.09 -142.59zM431.88 65.4297c53.7568 0.00292969 97.4004 43.6475 97.4004 97.4053c0 53.7598 -43.6455 97.4053 -97.4053 97.4053
|
||||
c-53.7588 0 -97.4053 -43.6455 -97.4053 -97.4053c0 -53.7578 43.6436 -97.4023 97.4004 -97.4053h0.00976562zM144.06 213.88c0.0175781 0 0.0332031 -0.000976562 0.0507812 -0.000976562c28.1299 0 50.9717 -22.8242 51 -50.9492v-0.109375
|
||||
c0 -28.1807 -22.8799 -51.0605 -51.0605 -51.0605s-51.0596 22.8799 -51.0596 51.0605s22.8789 51.0596 51.0596 51.0596h0.00976562zM431.88 213.88c28.1807 0 51.0605 -22.8789 51.0605 -51.0596s-22.8799 -51.0605 -51.0605 -51.0605s-51.0596 22.8799 -51.0596 51.0605
|
||||
s22.8789 51.0596 51.0596 51.0596z" />
|
||||
<glyph glyph-name="odnoklassniki" unicode="" horiz-adv-x="320"
|
||||
d="M275.1 114c-27.3994 -17.4004 -65.0996 -24.2998 -90 -26.9004l20.9004 -20.5996l76.2998 -76.2998c27.9004 -28.6006 -17.5 -73.2998 -45.7002 -45.7002c-19.0996 19.4004 -47.0996 47.4004 -76.2998 76.5996l-76.2998 -76.5
|
||||
c-28.2002 -27.5 -73.5996 17.6006 -45.4004 45.7002c19.4004 19.4004 47.1006 47.4004 76.3008 76.2998l20.5996 20.6006c-24.5996 2.59961 -62.9004 9.09961 -90.5996 26.8994c-32.6006 21 -46.9004 33.3008 -34.3008 59c7.40039 14.6006 27.7002 26.9004 54.6006 5.7002
|
||||
@ -1241,11 +1233,10 @@ d="M256 440c136.9 0 248 -111.1 248 -248s-111.1 -248 -248 -248s-248 111.1 -248 24
|
||||
h-116.9v-42.6006h70.1006c-5.2002 -34.2002 -37.5 -53.2998 -70.1006 -53.2998c-43 0 -77.2002 35.5 -77.2002 78.0996c0 42.6006 34.3008 78.1006 77.2002 78.1006c18.1006 0 36.2002 -6.2002 49.4004 -19.1006l33.5996 32.6006
|
||||
c-22.8994 21.2998 -51.7002 32.2998 -83 32.2998c-68.4375 0 -124 -55.5625 -124 -124s55.5625 -124 124 -124zM415.5 174.2h35.2002v35.5h-35.2002v35.5h-35.5v-35.5h-35.5v-35.5h35.5v-35.5h35.5v35.5z" />
|
||||
<glyph glyph-name="font-awesome" unicode=""
|
||||
d="M397.8 416c27.5 0 50.2002 -22.7002 50.2002 -50.2002v-347.6c0 -27.5 -22.7002 -50.2002 -50.2002 -50.2002h-347.6c-27.5 0 -50.2002 22.7002 -50.2002 50.2002v347.6c0 27.5 22.7002 50.2002 50.2002 50.2002h347.6zM352.4 131.7h0.0996094v140.3
|
||||
c0 4.2002 -4.2002 7.7998 -9 7.7998c-6 0 -31.0996 -16.0996 -53.7998 -16.0996c-4.7002 0 -8.90039 0.599609 -13.1006 2.39941c-20.2998 7.7002 -38.1992 13.7002 -60.8994 13.7002c-20.9004 0 -43 -6.5 -61.5 -14.2998
|
||||
c-1.7998 -1.2002 -3.60059 -1.7998 -5.40039 -2.40039v18.5c8.2998 6 13.1006 15.5 13.1006 26.3008c0 18.5996 -15 33.5 -33.5 33.5c-18.6006 0 -33.5 -15 -33.5 -33.5c0 -10.8008 5.2998 -20.3008 13.0996 -26.3008v-218.6c0 -11.2998 9 -20.2998 20.2998 -20.2998
|
||||
c8.90039 0 16.7002 5.89941 19.1006 14.2998v1.2002c0.599609 1.2002 0.599609 3 0.599609 4.7998v45.4004c1.2002 0.599609 2.40039 0.599609 3.59961 1.19922c19.7002 8.90039 44.2002 17.3008 67.5 17.3008c32.3008 0 44.8008 -16.7002 71.7002 -16.7002
|
||||
c19.2002 0 37.1006 6.5 53.7998 13.7002c4.2002 1.7998 7.80078 3.59961 7.80078 7.7998z" />
|
||||
d="M400 416c26.4922 0 48 -21.5078 48 -48v-352c0 -26.4922 -21.5078 -48 -48 -48h-352c-26.4922 0 -48 21.5078 -48 48v352c0 26.4922 21.5078 48 48 48h352zM336 136v160c-31.5996 -11.2002 -41.2002 -16 -59.7998 -16c-31.4004 0 -43.4004 16 -74.6006 16
|
||||
c-25.3994 0 -37.3994 -10.4004 -57.5996 -14.4004v6.40039c0 8.83105 -7.16895 16 -16 16s-16 -7.16895 -16 -16v-192c0 -8.83105 7.16895 -16 16 -16s16 7.16895 16 16v153.6c20.2002 4 32.2002 14.4004 57.5996 14.4004c31.4004 0 43.2002 -16 74.6006 -16
|
||||
c10.2002 0 17.7998 1.40039 27.7998 4.59961v-96c-10 -3.19922 -17.5996 -4.59961 -27.7998 -4.59961c-31.4004 0 -43.4004 16 -74.6006 16c-8.91309 -0.0322266 -17.5195 -1.44336 -25.5996 -4v-32c7.86035 2.58398 16.2559 4.00195 24.9756 4.00195
|
||||
c0.208008 0 0.416016 0 0.624023 -0.00195312c31.4004 0 43.2002 -16 74.6006 -16c18.5996 0 28.2002 4.7998 59.7998 16z" />
|
||||
<glyph glyph-name="linode" unicode=""
|
||||
d="M437.4 221.7c0.599609 -2 -8.80078 -66.2998 -9.7002 -72.7998c0 -0.900391 -0.5 -1.7002 -1.10059 -2l-54.5996 -43.7002c-1.09961 -0.900391 -2.59961 -0.900391 -3.7002 0l-20.2998 14l-2.2998 -33.4004c0 -0.899414 -0.200195 -1.7002 -1.10059 -2.2998
|
||||
l-66.8994 -53.4004c-1.10059 -0.899414 -2.90039 -0.899414 -4 0l-28 23.7002l2 -46c0 -0.899414 -0.200195 -1.7002 -1.10059 -2.2998l-83.6992 -66.9004c-0.600586 -0.299805 -1.10059 -0.599609 -1.7002 -0.599609c-0.900391 0.299805 -1.7002 0.299805 -2.2998 0.900391
|
||||
@ -1340,12 +1331,11 @@ c5.7002 -3.09961 6.90039 -9.40039 6 -15.0996c-1.09961 -9.7002 -28 -70.9004 -28.8
|
||||
c3.10059 0 8.30078 -0.900391 7.10059 -10.9004c-1.40039 -9.39941 -35.1006 -72.2998 -38.9004 -87.6992c-4.59961 -20.6006 6.60059 -41.4004 24.9004 -50.6006c11.3994 -5.7002 62.5 -15.7002 58.5 11.1006zM376.4 3.09961c10.5996 7.5 24.8994 4.60059 32.2998 -6
|
||||
c7.09961 -10.5996 4.59961 -25.1992 -6 -32.5996c-10.6006 -7.09961 -24.9004 -4.59961 -32 6c-7.2002 10.5996 -4.60059 25.2002 5.7002 32.5996z" />
|
||||
<glyph glyph-name="font-awesome-alt" unicode=""
|
||||
d="M339.3 276.8c5.40039 0 9.5 -3 7.7002 -7.09961v-134.4c0 -4.2002 -3 -6 -7.2002 -7.7998c-15.5996 -7.09961 -33.5 -13.7002 -52 -13.7002c-26.2998 0 -38.2002 16.1006 -69.2998 16.1006c-22.7002 0 -46 -8.30078 -65.7002 -16.7002
|
||||
c-0.599609 -0.600586 -1.7998 -1.2002 -3 -1.2002v-44.2002c0 -1.7998 0 -3 -0.599609 -4.7998v-1.2998c-2.40039 -7.7002 -9.5 -13.7002 -18.5 -13.7002c-10.7002 0 -19.7002 8.90039 -19.7002 19.7002v212.1c-7.7002 6 -12.5 15.5 -12.5 25.7002
|
||||
c0 18 14.2998 32.2998 32.2998 32.2998s32.2998 -14.3994 32.2998 -32.2998c0 -10.7998 -4.69922 -19.7002 -12.5 -25.7002v-17.8994c1.2002 0.599609 3 1.19922 4.80078 1.7998c17.8994 7.09961 39.3994 13.7002 59.6992 13.7002
|
||||
c22.1006 0 39.4004 -5.90039 59.1006 -13.7002c4.09961 -1.7998 8.2998 -2.40039 12.5 -2.40039c22.7002 0 46.5996 15.5 52.5996 15.5zM397.8 416c27.5 0 50.2002 -22.7002 50.2002 -50.2002v-347.6c0 -27.5 -22.7002 -50.2002 -50.2002 -50.2002h-347.6
|
||||
c-27.5 0 -50.2002 22.7002 -50.2002 50.2002v347.6c0 27.5 22.7002 50.2002 50.2002 50.2002h347.6zM412.1 18.2998v347.601c0 7.69922 -6.5 14.2998 -14.2998 14.2998v-0.100586h-347.6c-7.7002 0 -14.2998 -6.5 -14.2998 -14.2998v-347.5
|
||||
c0 -7.7002 6.5 -14.2998 14.2998 -14.2998h347.6c7.7002 0 14.2998 6.5 14.2998 14.2998z" />
|
||||
d="M400 416c26.4922 0 48 -21.5078 48 -48v-352c0 -26.4922 -21.5078 -48 -48 -48h-352c-26.4922 0 -48 21.5078 -48 48v352c0 26.4922 21.5078 48 48 48h352zM416 16v352c0 8.83105 -7.16895 16 -16 16h-352c-8.83105 0 -16 -7.16895 -16 -16v-352
|
||||
c0 -8.83105 7.16895 -16 16 -16h352c8.83105 0 16 7.16895 16 16zM201.6 296c31.2002 0 43.2002 -16 74.6006 -16c18.5996 0 28.2002 4.7998 59.7998 16v-160c-31.5996 -11.2002 -41.2002 -16 -59.7998 -16c-31.4004 0 -43.2002 16 -74.6006 16
|
||||
c-0.208008 0.00195312 -0.415039 -0.0175781 -0.623047 -0.0175781c-8.7207 0 -17.1162 -1.39844 -24.9766 -3.98242v32c8.08008 2.55664 16.6865 3.96777 25.5996 4c31.2002 0 43.2002 -16 74.6006 -16c10.2002 0 17.7998 1.40039 27.7998 4.59961v96
|
||||
c-10 -3.19922 -17.5996 -4.59961 -27.7998 -4.59961c-31.4004 0 -43.2002 16 -74.6006 16c-25.3994 0 -37.3994 -10.4004 -57.5996 -14.4004v-153.6c0 -8.83105 -7.16895 -16 -16 -16s-16 7.16895 -16 16v192c0 8.83105 7.16895 16 16 16s16 -7.16895 16 -16v-6.40039
|
||||
c20.2002 4 32.2002 14.4004 57.5996 14.4004z" />
|
||||
<glyph glyph-name="accessible-icon" unicode=""
|
||||
d="M423.9 192.2l-12.9004 -157.3c-3.2998 -40.7002 -63.9004 -35.1006 -60.5996 4.89941l10 122.5l-41.1006 -2.2998c10.1006 -20.7002 15.7998 -43.9004 15.7998 -68.5c0 -41.2002 -16.0996 -78.7002 -42.2998 -106.5l-39.2998 39.2998
|
||||
c57.9004 63.7002 13.0996 167.2 -74 167.2c-25.9004 0 -49.5 -9.90039 -67.2002 -26l-39.2998 39.2998c22 20.7002 50.0996 35.1006 81.4004 40.2002l75.2998 85.7002l-42.6006 24.7998l-51.5996 -46c-30 -26.7998 -70.5996 18.5 -40.5 45.4004l68 60.6992
|
||||
@ -1590,14 +1580,18 @@ c13.2998 45.5 -42.2002 71.7002 -64 29.2998z" />
|
||||
d="M87 -33.7998v73.5996h73.7002v-73.5996h-73.7002zM25.4004 101.4h61.5996v-61.6006h-61.5996v61.6006zM491.6 271.1c53.2002 -170.3 -73 -327.1 -235.6 -327.1v95.7998h0.299805v0.299805c101.7 0.200195 180.5 101 141.4 208
|
||||
c-14.2998 39.6006 -46.1006 71.4004 -85.7998 85.7002c-107.101 38.7998 -208.101 -39.8994 -208.101 -141.7h-95.7998c0 162.2 156.9 288.7 327 235.601c74.2002 -23.2998 133.6 -82.4004 156.6 -156.601zM256.3 40.0996h-0.299805v-0.299805h-95.2998v95.6006h95.5996
|
||||
v-95.3008z" />
|
||||
<glyph glyph-name="discord" unicode=""
|
||||
d="M297.216 204.8c0 -15.6162 -11.5195 -28.416 -26.1123 -28.416c-14.3359 0 -26.1113 12.7998 -26.1113 28.416s11.5195 28.416 26.1113 28.416c14.5928 0 26.1123 -12.7998 26.1123 -28.416zM177.664 233.216c14.5918 0 26.3682 -12.7998 26.1123 -28.416
|
||||
c0 -15.6162 -11.5205 -28.416 -26.1123 -28.416c-14.3359 0 -26.1123 12.7998 -26.1123 28.416s11.5205 28.416 26.1123 28.416zM448 395.264v-459.264c-64.4941 56.9941 -43.8682 38.1279 -118.784 107.776l13.5684 -47.3604h-290.304
|
||||
c-28.9287 0 -52.4805 23.5518 -52.4805 52.7363v346.111c0 29.1846 23.5518 52.7363 52.4805 52.7363h343.039c28.9287 0 52.4805 -23.5518 52.4805 -52.7363zM375.04 152.576c0 82.4316 -36.8643 149.248 -36.8643 149.248
|
||||
c-36.8643 27.6475 -71.9355 26.8799 -71.9355 26.8799l-3.58398 -4.0957c43.5195 -13.3125 63.7441 -32.5127 63.7441 -32.5127c-60.8115 33.3291 -132.244 33.335 -191.232 7.42383c-9.47168 -4.35156 -15.1035 -7.42383 -15.1035 -7.42383
|
||||
s21.2471 20.2246 67.3271 33.5361l-2.55957 3.07227s-35.0723 0.767578 -71.9355 -26.8799c0 0 -36.8643 -66.8164 -36.8643 -149.248c0 0 21.5039 -37.1201 78.0801 -38.9121c0 0 9.47168 11.5195 17.1514 21.248c-32.5117 9.72754 -44.7998 30.208 -44.7998 30.208
|
||||
c3.7666 -2.63574 9.97656 -6.05273 10.4961 -6.40039c43.21 -24.1973 104.588 -32.126 159.744 -8.95996c8.95996 3.32812 18.9443 8.19238 29.4395 15.1045c0 0 -12.7998 -20.9922 -46.3359 -30.4639c7.68066 -9.72852 16.8965 -20.7363 16.8965 -20.7363
|
||||
c56.5762 1.79199 78.3359 38.9121 78.3359 38.9121z" />
|
||||
<glyph glyph-name="discord" unicode="" horiz-adv-x="640"
|
||||
d="M524.531 378.164c66.4014 -97.6289 99.1973 -207.758 86.9336 -334.541c-0.0498047 -0.554688 -0.338867 -1.04102 -0.764648 -1.35156c-43.8203 -32.4541 -93.7129 -57.8623 -147.062 -74.1865c-0.171875 -0.0527344 -0.354492 -0.0830078 -0.543945 -0.0830078
|
||||
c-0.625977 0 -1.18066 0.308594 -1.51855 0.783203c-11.1562 15.4766 -21.1797 31.7598 -30.0146 48.8145c-0.131836 0.256836 -0.208984 0.549805 -0.208984 0.858398c0 0.799805 0.50293 1.48438 1.20898 1.75293c15.916 5.9834 31.3828 13.3604 45.8906 21.8301
|
||||
c0.550781 0.329102 0.918945 0.928711 0.918945 1.61621c0 0.617188 -0.297852 1.16602 -0.756836 1.50977c-3.10547 2.30859 -6.18848 4.73438 -9.13184 7.16016c-0.3125 0.254883 -0.713867 0.407227 -1.14844 0.407227
|
||||
c-0.277344 0 -0.541016 -0.0625 -0.776367 -0.174805c-95.0898 -43.917 -199.271 -43.917 -295.5 0c-0.226562 0.101562 -0.480469 0.15918 -0.744141 0.15918c-0.438477 0 -0.84082 -0.15625 -1.15527 -0.415039c-2.94336 -2.42578 -6.02734 -4.82812 -9.10938 -7.13672
|
||||
c-0.453125 -0.344727 -0.74707 -0.886719 -0.74707 -1.5c0 -0.692383 0.375 -1.29883 0.932617 -1.62598c14.5459 -8.40234 30 -15.7812 45.8672 -21.8525c0.712891 -0.261719 1.21973 -0.946289 1.21973 -1.74902c0 -0.301758 -0.0722656 -0.586914 -0.200195 -0.839844
|
||||
c-8.69238 -17.1572 -18.7334 -33.4609 -30.0371 -48.8418c-0.34668 -0.459961 -0.896484 -0.755859 -1.5166 -0.755859c-0.19043 0 -0.373047 0.0283203 -0.546875 0.0800781c-53.25 16.3789 -103.055 41.7812 -146.824 74.1895
|
||||
c-0.419922 0.327148 -0.706055 0.817383 -0.765625 1.375c-10.2441 109.663 10.6387 220.702 86.8672 334.54c0.185547 0.300781 0.459961 0.537109 0.788086 0.676758c37.3066 17.1338 78.0146 29.9219 119.688 37.1064
|
||||
c0.0957031 0.015625 0.191406 0.0253906 0.292969 0.0253906c0.694336 0 1.30176 -0.375977 1.63086 -0.935547c5.56348 -9.8418 10.6553 -20.126 15.1348 -30.5996c22.0664 3.34961 43.7744 5.08691 66.7705 5.08691c22.9951 0 45.5889 -1.7373 67.6553 -5.08691
|
||||
c4.44727 10.4414 9.46191 20.7285 14.9004 30.5996c0.308594 0.5625 0.90332 0.941406 1.58887 0.941406c0.114258 0 0.225586 -0.0107422 0.333984 -0.03125c41.666 -7.19922 82.373 -19.9863 119.686 -37.1055c0.331055 -0.135742 0.601562 -0.384766 0.764648 -0.700195z
|
||||
M222.491 110.42c29.4326 0 52.8428 26.5869 52.8428 59.2412c0.462891 32.4189 -23.1777 59.2393 -52.8428 59.2393c-29.4355 0 -52.8438 -26.5898 -52.8438 -59.2412c0 -32.6523 23.8711 -59.2393 52.8438 -59.2393zM417.871 110.42
|
||||
c29.667 0 52.8438 26.5869 52.8438 59.2412c0.462891 32.4189 -23.1768 59.2393 -52.8438 59.2393c-29.4346 0 -52.8428 -26.5898 -52.8428 -59.2412c0 -32.6523 23.8721 -59.2393 52.8428 -59.2393z" />
|
||||
<glyph glyph-name="discourse" unicode=""
|
||||
d="M225.9 416c122.699 0 222.1 -102.3 222.1 -223.9c0 -121.6 -99.4004 -223.899 -222.1 -223.899l-225.801 -0.200195s-0.0996094 224 -0.0996094 227.9c0 121.6 103.3 220.1 225.9 220.1zM224 64c70.7002 0 128 57.2998 128 128s-57.2998 128 -128 128
|
||||
s-128 -57.2998 -128 -128c0 -22.0996 5.59961 -42.9004 15.4004 -61l-22.9004 -75l81.0996 20.0996c16.5 -7.7998 35 -12.0996 54.4004 -12.0996z" />
|
||||
@ -2461,10 +2455,11 @@ c13.7002 9.39941 16.4004 24.3994 9.10059 31.3994c-7.2002 6.90039 -28.2002 -7 -29
|
||||
c12.5996 33.0996 -3.59961 45.5 -3.59961 45.5s-23.4004 12.9004 -33.3008 -20.2002c-9.89941 -33.0996 -6.39941 -44.8994 -6.39941 -44.8994s30.7002 -13.4004 43.2998 19.5996zM442.1 188.1c0 0 15.7002 -1.09961 26.4004 14.2002s1.2998 25.5 1.2998 25.5
|
||||
s-8.59961 11.1006 -19.5996 -9.09961c-11.1006 -20.1006 -8.10059 -30.6006 -8.10059 -30.6006z" />
|
||||
<glyph glyph-name="font-awesome-flag" unicode=""
|
||||
d="M444.373 88.5762c0 -7.16797 -6.14453 -10.2402 -13.3125 -13.3125c-28.6719 -12.2881 -59.3916 -23.5518 -92.1592 -23.5518c-46.0801 0 -67.584 28.6719 -122.88 28.6719c-39.9365 0 -81.9209 -14.3359 -115.713 -29.6953
|
||||
c-2.04785 -1.02441 -4.0957 -1.02441 -6.14355 -2.04883v-77.8232c0 -21.4053 -16.1221 -34.8164 -33.792 -34.8164c-19.4561 0 -34.8164 15.3604 -34.8164 34.8164v374.783c-13.3115 10.2402 -22.5273 26.624 -22.5273 45.0566c0 31.7441 25.5996 57.3438 57.3438 57.3438
|
||||
s57.3438 -25.5996 57.3438 -57.3438c0 -18.4326 -8.19141 -34.8164 -22.5273 -45.0566v-31.7432c4.12402 1.37402 58.7676 28.6719 114.688 28.6719c65.2705 0 97.6758 -27.6484 126.976 -27.6484c38.9121 0 81.9209 27.6484 92.1602 27.6484
|
||||
c8.19238 0 15.3604 -6.14453 15.3604 -13.3125v-240.64z" />
|
||||
d="M448 400v-336c-63 -23 -82 -32 -119 -32c-63 0 -87 32 -150 32c-20 0 -36 -4 -51 -8v64c15 4 31 8 51 8c63 0 87 -32 150 -32c20 0 35 3 55 9v208c-20 -6 -35 -9 -55 -9c-63 0 -87 32 -150 32c-51 0 -75 -21 -115 -29v-307
|
||||
c0.00195312 -0.136719 0.00292969 -0.273438 0.00292969 -0.410156c0 -17.4404 -14.1602 -31.5996 -31.6006 -31.5996c-0.136719 0 -0.265625 0.0078125 -0.402344 0.00976562c-0.136719 -0.00195312 -0.273438 -0.00292969 -0.410156 -0.00292969
|
||||
c-17.4404 0 -31.5996 14.1602 -31.5996 31.6006c0 0.136719 0.0078125 0.265625 0.00976562 0.402344v384c-0.00195312 0.136719 -0.00292969 0.273438 -0.00292969 0.410156c0 17.4404 14.1602 31.5996 31.6006 31.5996
|
||||
c0.136719 0 0.265625 -0.0078125 0.402344 -0.00976562c0.136719 0.00195312 0.273438 0.00292969 0.410156 0.00292969c17.4404 0 31.5996 -14.1602 31.5996 -31.6006c0 -0.136719 -0.0078125 -0.265625 -0.00976562 -0.402344v-13c40 8 64 29 115 29c63 0 87 -32 150 -32
|
||||
c37 0 56 9 119 32z" />
|
||||
<glyph glyph-name="gitter" unicode="" horiz-adv-x="384"
|
||||
d="M66.4004 125.5h-50.4004v322.5h50.4004v-322.5zM166.9 371.9v-435.9h-50.4004v435.9h50.4004zM267.5 371.9v-435.9h-50.4004v435.9h50.4004zM368 372v-247h-50.4004v247h50.4004z" />
|
||||
<glyph glyph-name="hooli" unicode="" horiz-adv-x="640"
|
||||
@ -3425,9 +3420,13 @@ M353.9 173.3c3.55273 2.83594 6.87891 5.7998 10.0996 9l-34.9004 35c-3.18457 -3.22
|
||||
c2.53027 3.79688 4.77832 7.81738 6.7002 12l-39.5 39.7998c-0.374023 -5.3252 -1.63574 -10.4893 -3.59961 -15.2002zM391.6 230.8l-53.0996 53.4004c4.25977 -7.79688 6.82422 -16.7627 7.09961 -26.2002l41.3008 -41.5c1.7959 4.61523 3.39258 9.46387 4.69922 14.2998z
|
||||
M392.6 236.4c1.25586 5.3623 2.04199 10.9189 2.30078 16.5996l-64.3008 64.7002c-2.61426 -3.74805 -5.95898 -6.85938 -9.89941 -9.2002z" />
|
||||
<glyph glyph-name="figma" unicode="" horiz-adv-x="384"
|
||||
d="M277 277.3h-85.4004v-256c-0.0273438 -47.082 -38.2617 -85.2998 -85.3506 -85.2998c-47.1055 0 -85.3496 38.2441 -85.3496 85.3496c0 47.1064 38.2441 85.3506 85.3496 85.3506h0.0507812c-47.1055 0 -85.3496 38.2441 -85.3496 85.3496
|
||||
c0 47.1064 38.2441 85.3506 85.3496 85.3506c-47.0781 0 -85.2998 38.2217 -85.2998 85.2998s38.2217 85.2998 85.2998 85.2998h170.7c47.1055 0 85.3496 -38.2441 85.3496 -85.3496c0 -47.1064 -38.2441 -85.3506 -85.3496 -85.3506zM277 277.3
|
||||
c47.0762 -0.00488281 85.2949 -38.2236 85.2998 -85.2998c0 -47.0781 -38.2217 -85.2998 -85.2998 -85.2998s-85.2998 38.2217 -85.2998 85.2998s38.2217 85.2998 85.2998 85.2998z" />
|
||||
d="M14 352.208c0 52.9043 42.8877 95.792 95.793 95.792h164.368c52.9053 0 95.793 -42.8877 95.793 -95.792c0 -33.5 -17.1963 -62.9844 -43.2432 -80.1055c26.0469 -17.1211 43.2432 -46.6045 43.2432 -80.1045c0 -52.9053 -42.8877 -95.793 -95.793 -95.793h-2.08008
|
||||
c-24.8018 0 -47.4033 9.42578 -64.415 24.8906v-88.2627c0 -53.6104 -44.0088 -96.833 -97.3574 -96.833c-52.7725 0 -96.3086 42.7568 -96.3086 95.793c0 33.498 17.1943 62.9805 43.2393 80.1016c-26.0449 17.1221 -43.2393 46.6055 -43.2393 80.1035
|
||||
c0 33.5 17.1963 62.9834 43.2422 80.1045c-26.0459 17.1211 -43.2422 46.6055 -43.2422 80.1055zM176.288 256.413h-66.4951c-35.5762 0 -64.415 -28.8398 -64.415 -64.415c0 -35.4385 28.6172 -64.1924 64.0029 -64.4141
|
||||
c0.136719 0.000976562 0.274414 0.000976562 0.412109 0.000976562h66.4951v128.828zM207.666 191.998c0 -35.5752 28.8389 -64.415 64.415 -64.415h2.08008c35.5762 0 64.415 28.8398 64.415 64.415s-28.8389 64.415 -64.415 64.415h-2.08008
|
||||
c-35.5762 0 -64.415 -28.8398 -64.415 -64.415zM109.793 96.2051c-0.137695 0 -0.275391 0.000976562 -0.412109 0.000976562c-35.3857 -0.220703 -64.0029 -28.9746 -64.0029 -64.4131c0 -35.4453 29.2246 -64.415 64.9307 -64.415
|
||||
c36.2822 0 65.9795 29.4365 65.9795 65.4551v63.3721h-66.4951zM109.793 416.622c-35.5762 0 -64.415 -28.8398 -64.415 -64.4141c0 -35.5762 28.8389 -64.415 64.415 -64.415h66.4951v128.829h-66.4951zM207.666 287.793h66.4951c35.5762 0 64.415 28.8389 64.415 64.415
|
||||
c0 35.5742 -28.8389 64.4141 -64.415 64.4141h-66.4951v-128.829z" />
|
||||
<glyph glyph-name="intercom" unicode=""
|
||||
d="M392 416c30.9004 0 56 -25.0996 56 -56v-336c0 -30.9004 -25.0996 -56 -56 -56h-336c-30.9004 0 -56 25.0996 -56 56v336c0 30.9004 25.0996 56 56 56h336zM283.7 333.9v-199.5c0 -19.8008 29.8994 -19.8008 29.8994 0v199.5c0 19.7998 -29.8994 19.7998 -29.8994 0z
|
||||
M209.1 341.4v-216.5c0 -19.8008 29.9004 -19.8008 29.9004 0v216.5c0 19.7998 -29.9004 19.7998 -29.9004 0zM134.4 333.9v-199.5c0 -19.8008 29.8994 -19.8008 29.8994 0v199.5c0 19.7998 -29.8994 19.7998 -29.8994 0zM59.7002 304v-134.3
|
||||
@ -3553,11 +3552,12 @@ c30.4102 17.9199 81.0498 55.6504 132.75 115.92c14.9697 -9 16.1494 -11.71 16.5098
|
||||
c-0.860352 -1.67969 -20.0303 -21.6797 -63.2803 -20.4092c5.5 -12.9404 10.9902 -25.0908 16.5 -36.4404zM306.579 337c-1.58008 2.4502 -39.5801 58.8496 -56.4805 54.6104c-16.8994 1.09961 -36.21 -22.9805 -38.21 -75.2803
|
||||
c21.1104 13.2402 50.1299 22.3301 94.6904 20.6699zM175.929 333.9c-3.7998 6.68945 -8.66992 12.4795 -14.4297 13.5693h-0.0898438c-24.79 1.41016 -24.75 -52.8301 -24.6699 -49.5898c13.6602 -0.00976562 27.8496 -0.410156 42.3994 -1.25977
|
||||
c-1.62012 12.6602 -2.72949 25.1699 -3.20996 37.2803zM147.869 171.9c-30.7998 -61.5098 -19.8701 -76.6104 -19.6699 -76.8203c7.38965 -15.4902 38.1299 -20.25 84.9199 4.50977c-21.9502 11.7402 -44.4902 32.6104 -65.25 72.3105zM357.929 97.0996z" />
|
||||
<glyph glyph-name="bootstrap" unicode=""
|
||||
d="M292.3 136.07c0 -42.4102 -39.7197 -41.4307 -43.9199 -41.4307h-80.8896v81.6904h80.8896c42.5605 0 43.9199 -31.9004 43.9199 -40.2598zM242.15 209.2h-74.6602v72.1797h74.6602c34.9297 0 38.4395 -20.3496 38.4395 -35.8701
|
||||
c0 -37.3096 -37.7695 -36.3096 -38.4395 -36.3096zM448 341.33v-298.66c-0.121094 -41.1553 -33.5146 -74.5488 -74.6699 -74.6699h-298.66c-41.1553 0.121094 -74.5488 33.5146 -74.6699 74.6699v298.66c0.121094 41.1553 33.5146 74.5488 74.6699 74.6699h298.66
|
||||
c41.1553 -0.121094 74.5488 -33.5146 74.6699 -74.6699zM338.05 130.14c0 21.5703 -6.64941 58.29 -49.0498 67.3506v0.729492c22.9102 9.78027 37.3398 28.25 37.3398 55.6406c0 7 2 64.7793 -77.5996 64.7793h-127v-261.33c128.229 0 139.87 -1.67969 163.6 5.70996
|
||||
c14.21 4.4209 52.71 17.9805 52.71 67.1201z" />
|
||||
<glyph glyph-name="bootstrap" unicode="" horiz-adv-x="576"
|
||||
d="M333.5 246.6c0 -23.5996 -18.0996 -36.7998 -50.9004 -36.8994h-42.5v71.2002h50.4004c27.4004 0 43 -12.2002 43 -34.3008zM517 259.4c9.5 -31 25.7002 -50.6006 52 -53.1006v-28.5c-26.4004 -2.5 -42.5 -22.0996 -52.0996 -53.0996
|
||||
c-9.5 -30.9004 -10.8008 -68.7998 -9.80078 -98.1006c1.10059 -30.3994 -22.5996 -58.5 -54.6992 -58.5h-328.7c-32 0 -55.7998 28 -54.7002 58.5c1.09961 29.3008 -0.299805 67.2002 -9.7998 98.1006c-9.60059 31 -25.7998 50.5996 -52.2002 53.0996v28.5
|
||||
c26.5 2.5 42.5996 22.1006 52.2002 53.1006c9.5 30.8994 10.7998 68.7998 9.7998 98.0996c-1.09961 30.4004 22.5996 58.5 54.7002 58.5h328.8c32 0 55.7998 -28 54.7002 -58.5c-1.10059 -29.2998 0.299805 -67.2002 9.7998 -98.0996zM300.2 72.9004
|
||||
c51.8994 0 83.2002 25.3994 83.2002 67.5c0 31.6992 -22.3008 54.6992 -55.5 58.2998v1.2998c24.3994 3.90039 43.5 26.5 43.5 51.7998c0 36 -28.4004 59.4004 -71.7002 59.4004h-97.4004v-238.3h97.9004zM290.2 181.6c35.8994 0 54.5 -13.1992 54.5 -38.8994
|
||||
c0 -25.7998 -18.1006 -39.5 -52.2998 -39.5h-52.3008v78.3994h50.1006z" />
|
||||
<glyph glyph-name="buffer" unicode=""
|
||||
d="M427.84 67.3301l-196.5 -97.8203c-2.24707 -0.963867 -4.72266 -1.49805 -7.32129 -1.49805s-5.10156 0.53418 -7.34863 1.49805l-196.51 97.8203c-4 2 -4 5.28027 0 7.29004l47.0596 23.3799c2.25098 0.964844 4.72949 1.49805 7.33203 1.49805
|
||||
c2.60156 0 5.10742 -0.533203 7.3584 -1.49805l134.76 -67c2.24609 -0.969727 4.72168 -1.50684 7.32129 -1.50684s5.10254 0.537109 7.34863 1.50684l134.76 67c2.24902 0.964844 4.72656 1.49902 7.32715 1.49902s5.10449 -0.53418 7.35352 -1.49902l47.0596 -23.4297
|
||||
|
Before Width: | Height: | Size: 730 KiB After Width: | Height: | Size: 730 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -2,11 +2,11 @@
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1">
|
||||
<metadata>
|
||||
Created by FontForge 20201107 at Tue Mar 16 10:15:04 2021
|
||||
Created by FontForge 20201107 at Wed Aug 4 12:25:29 2021
|
||||
By Robert Madole
|
||||
Copyright (c) Font Awesome
|
||||
</metadata>
|
||||
<!-- Font Awesome Free 5.15.3 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><defs>
|
||||
<!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><defs>
|
||||
<font id="FontAwesome5Free-Regular" horiz-adv-x="512" >
|
||||
<font-face
|
||||
font-family="Font Awesome 5 Free Regular"
|
||||
|
Before Width: | Height: | Size: 141 KiB After Width: | Height: | Size: 141 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -2,11 +2,11 @@
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1">
|
||||
<metadata>
|
||||
Created by FontForge 20201107 at Tue Mar 16 10:15:04 2021
|
||||
Created by FontForge 20201107 at Wed Aug 4 12:25:29 2021
|
||||
By Robert Madole
|
||||
Copyright (c) Font Awesome
|
||||
</metadata>
|
||||
<!-- Font Awesome Free 5.15.3 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><defs>
|
||||
<!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><defs>
|
||||
<font id="FontAwesome5Free-Solid" horiz-adv-x="512" >
|
||||
<font-face
|
||||
font-family="Font Awesome 5 Free Solid"
|
||||
|
Before Width: | Height: | Size: 898 KiB After Width: | Height: | Size: 898 KiB |
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user