redmineup 1.0.4 → 1.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/app/assets/images/icons.svg +8 -0
- data/app/assets/javascripts/chart.min.js +13 -0
- data/{vendor → app}/assets/javascripts/select2_helpers.js +1 -1
- data/{vendor → app}/assets/stylesheets/calendars.css +3 -3
- data/{vendor → app}/assets/stylesheets/money.css +1 -2
- data/{vendor → app}/assets/stylesheets/select2.css +1 -1
- data/doc/CHANGELOG +26 -4
- data/lib/redmineup/acts_as_votable/votable.rb +2 -5
- data/lib/redmineup/assets_manager.rb +12 -1
- data/lib/redmineup/currency.rb +3 -1
- data/lib/redmineup/helpers/external_assets_helper.rb +1 -1
- data/lib/redmineup/liquid/drops/attachment_drop.rb +8 -0
- data/lib/redmineup/liquid/drops/custom_field_enumeration_drop.rb +11 -0
- data/lib/redmineup/liquid/drops/issues_drop.rb +11 -19
- data/lib/redmineup/liquid/drops/users_drop.rb +2 -3
- data/lib/redmineup/liquid/drops/version_drop.rb +40 -0
- data/lib/redmineup/liquid/filters/additional.rb +31 -0
- data/lib/redmineup/liquid/filters/base.rb +16 -1
- data/lib/redmineup/patches/compatibility/sprite_patch.rb +21 -0
- data/lib/redmineup/patches/compatibility_patch.rb +4 -1
- data/lib/redmineup/version.rb +1 -1
- data/lib/redmineup.rb +22 -2
- data/test/liquid/drops/issues_drop_test.rb +5 -0
- data/test/liquid/filters/additional_filter_test.rb +31 -0
- data/test/models/attachment.rb +5 -0
- data/test/models/issue.rb +5 -0
- data/test/models/journal.rb +6 -0
- data/test/schema.rb +64 -57
- metadata +20 -10
- /data/{vendor → app}/assets/images/money.png +0 -0
- /data/{vendor → app}/assets/images/vcard.png +0 -0
- /data/{vendor/assets/javascripts/Chart.bundle.min.js → app/assets/javascripts/Chart.bundle.min.js.bak} +0 -0
- /data/{vendor → app}/assets/javascripts/select2.js +0 -0
data/doc/CHANGELOG
CHANGED
@@ -1,11 +1,33 @@
|
|
1
|
-
==
|
1
|
+
== RedmineUP gem changelog
|
2
2
|
|
3
3
|
Redmine UP gem - general functions for plugins (tags, vote, viewing, currency)
|
4
|
-
Copyright (C) 2011-
|
4
|
+
Copyright (C) 2011-2025 Kirill Bezrukov (RedmineUP)
|
5
5
|
https://www.redmineup.com/
|
6
6
|
|
7
|
-
==
|
7
|
+
== 2025-02-04 v1.0.9
|
8
8
|
|
9
|
+
* Added regex_replace Liquid filter
|
10
|
+
* Added svg icons for Redmine 6
|
11
|
+
|
12
|
+
== 2025-01-31 v1.0.8
|
13
|
+
|
14
|
+
* Fixed compatibility patch for Redmine < 5
|
15
|
+
|
16
|
+
== 2025-01-29 v1.0.7
|
17
|
+
|
18
|
+
* Fixed parse_inline_attachments Liquid filter
|
19
|
+
* Added compatibility patch for Redmine 6 sprite_icon
|
20
|
+
|
21
|
+
== 2024-05-07 v1.0.6
|
22
|
+
|
23
|
+
* Updated Chart.js to v3.9.1
|
24
|
+
* Fixed assets paths for Redmine 5.3+
|
25
|
+
* Added attachments to IssueDrop and JournalDrop
|
26
|
+
* Added filter parse_inline_attachments for replace image urls
|
27
|
+
|
28
|
+
== 2024-01-31 v1.0.5
|
29
|
+
|
30
|
+
* Added plugin_installed? method
|
9
31
|
* Compatibility patches for ApplicationRecord
|
10
32
|
|
11
33
|
== 2023-10-12 v1.0.1
|
@@ -15,4 +37,4 @@ https://www.redmineup.com/
|
|
15
37
|
== 2023-09-27 v1.0.0
|
16
38
|
|
17
39
|
* Initial release
|
18
|
-
* Mirgated from redmineup gem
|
40
|
+
* Mirgated from redmineup gem
|
@@ -232,11 +232,8 @@ module Redmineup
|
|
232
232
|
end
|
233
233
|
end
|
234
234
|
self.record_timestamps = false
|
235
|
-
|
236
|
-
|
237
|
-
else
|
238
|
-
self.assign_attributes(updates) && self.save if !updates.empty?
|
239
|
-
end
|
235
|
+
|
236
|
+
self.update(updates) if !updates.empty?
|
240
237
|
end
|
241
238
|
|
242
239
|
# results
|
@@ -2,7 +2,8 @@ module Redmineup
|
|
2
2
|
class AssetsManager
|
3
3
|
def self.install_assets
|
4
4
|
return unless Gem.loaded_specs[GEM_NAME]
|
5
|
-
|
5
|
+
|
6
|
+
source = File.join(Gem.loaded_specs[GEM_NAME].full_gem_path, 'app', 'assets')
|
6
7
|
destination = File.join(Dir.pwd, 'public', 'plugin_assets', GEM_NAME)
|
7
8
|
return unless File.directory?(source)
|
8
9
|
|
@@ -39,5 +40,15 @@ module Redmineup
|
|
39
40
|
end
|
40
41
|
end
|
41
42
|
end
|
43
|
+
|
44
|
+
def self.base_path
|
45
|
+
Gem.loaded_specs[GEM_NAME].full_gem_path
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.assets_paths
|
49
|
+
return [] unless Gem.loaded_specs[GEM_NAME]
|
50
|
+
|
51
|
+
Dir[File.join(base_path, 'app', 'assets', '*')]
|
52
|
+
end
|
42
53
|
end
|
43
54
|
end
|
data/lib/redmineup/currency.rb
CHANGED
@@ -40,7 +40,9 @@ module Redmineup
|
|
40
40
|
Redmine::MenuManager.map(:admin_menu).push(:redmineup_money,
|
41
41
|
{ controller: 'redmineup', action: 'settings', id: 'money' },
|
42
42
|
caption: :label_redmineup_money,
|
43
|
-
html: { class: 'icon icon-redmineup-money' }
|
43
|
+
html: { class: 'icon icon-redmineup-money' },
|
44
|
+
icon: 'money',
|
45
|
+
plugin: :redmineup)
|
44
46
|
|
45
47
|
end
|
46
48
|
|
@@ -35,6 +35,7 @@ module Redmineup
|
|
35
35
|
|
36
36
|
delegate :id,
|
37
37
|
:subject,
|
38
|
+
:description,
|
38
39
|
:visible?,
|
39
40
|
:closed?,
|
40
41
|
:start_date,
|
@@ -71,6 +72,10 @@ module Redmineup
|
|
71
72
|
@assignee ||= UserDrop.new(@issue.assigned_to)
|
72
73
|
end
|
73
74
|
|
75
|
+
def attachments
|
76
|
+
@attachments ||= @issue.attachments.map { |attachment| AttachmentDrop.new(attachment) }
|
77
|
+
end
|
78
|
+
|
74
79
|
def tracker
|
75
80
|
@tracker ||= @issue.tracker && @issue.tracker.name
|
76
81
|
end
|
@@ -103,10 +108,6 @@ module Redmineup
|
|
103
108
|
@project ||= ProjectDrop.new @issue.project if @issue.project
|
104
109
|
end
|
105
110
|
|
106
|
-
def description
|
107
|
-
@description ||= replace_images_urls(@issue.description)
|
108
|
-
end
|
109
|
-
|
110
111
|
def subtasks
|
111
112
|
@subtasks ||= IssuesDrop.new @issue.children
|
112
113
|
end
|
@@ -120,11 +121,11 @@ module Redmineup
|
|
120
121
|
end
|
121
122
|
|
122
123
|
def notes
|
123
|
-
@notes ||= @issue.journals.where.not(notes: [nil, '']).order(:created_on).map(&:notes)
|
124
|
+
@notes ||= @issue.journals.where.not(notes: [nil, '']).order(:created_on).map(&:notes)
|
124
125
|
end
|
125
126
|
|
126
127
|
def journals
|
127
|
-
@journals ||= JournalsDrop.new
|
128
|
+
@journals ||= JournalsDrop.new @issue.journals.where.not(notes: [nil, ''])
|
128
129
|
end
|
129
130
|
|
130
131
|
def tags
|
@@ -158,19 +159,6 @@ module Redmineup
|
|
158
159
|
def custom_field_values
|
159
160
|
@issue.custom_field_values
|
160
161
|
end
|
161
|
-
|
162
|
-
private
|
163
|
-
|
164
|
-
def replace_images_urls(text)
|
165
|
-
text.gsub(/\!.*\!/) do |i_name|
|
166
|
-
i_name = i_name.delete('!')
|
167
|
-
i_name_css = i_name.scan(/^\{.*\}/).first.to_s
|
168
|
-
attachment = @issue.attachments.find_by(filename: i_name.gsub(i_name_css, ''))
|
169
|
-
image = AttachmentDrop.new attachment if attachment
|
170
|
-
attach_url = image.try(:url)
|
171
|
-
attach_url ? "!#{i_name_css}#{attach_url}!" : i_name
|
172
|
-
end
|
173
|
-
end
|
174
162
|
end
|
175
163
|
|
176
164
|
class JournalsDrop < ::Liquid::Drop
|
@@ -212,6 +200,10 @@ module Redmineup
|
|
212
200
|
def issue
|
213
201
|
@issue ||= IssueDrop.new @journal.issue if @journal.issue
|
214
202
|
end
|
203
|
+
|
204
|
+
def attachments
|
205
|
+
@attachments ||= @journal.attachments.map { |attachment| AttachmentDrop.new(attachment) }
|
206
|
+
end
|
215
207
|
end
|
216
208
|
end
|
217
209
|
end
|
@@ -35,7 +35,7 @@ module Redmineup
|
|
35
35
|
end
|
36
36
|
|
37
37
|
class UserDrop < ::Liquid::Drop
|
38
|
-
delegate :id, :name, :firstname, :lastname, :mail, :active?, :admin?, :logged?, :language, :to => :@user, allow_nil: true
|
38
|
+
delegate :id, :name, :firstname, :lastname, :mail, :active?, :admin?, :logged?, :language, :to_s, :to => :@user, allow_nil: true
|
39
39
|
|
40
40
|
def initialize(user)
|
41
41
|
@user = user
|
@@ -61,8 +61,7 @@ module Redmineup
|
|
61
61
|
|
62
62
|
def custom_field_values
|
63
63
|
@user.custom_field_values
|
64
|
-
end
|
65
|
-
|
64
|
+
end
|
66
65
|
end
|
67
66
|
end
|
68
67
|
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Redmineup
|
2
|
+
module Liquid
|
3
|
+
class VersionDrop < ::Liquid::Drop
|
4
|
+
delegate :id,
|
5
|
+
:name,
|
6
|
+
:description,
|
7
|
+
:effective_date,
|
8
|
+
:due_date,
|
9
|
+
:wiki_page_title,
|
10
|
+
:status,
|
11
|
+
:sharing,
|
12
|
+
:default_project_version,
|
13
|
+
:start_date,
|
14
|
+
:due_date,
|
15
|
+
:estimated_hours,
|
16
|
+
:spent_hours,
|
17
|
+
:closed?,
|
18
|
+
:open?,
|
19
|
+
:completed?,
|
20
|
+
:completed_percent,
|
21
|
+
:closed_percent,
|
22
|
+
:overdue?,
|
23
|
+
:issues_count,
|
24
|
+
:open_issues_count,
|
25
|
+
:closed_issues_count,
|
26
|
+
:visible_fixed_issues,
|
27
|
+
:wiki_page,
|
28
|
+
:to_s_with_project,
|
29
|
+
:shared?,
|
30
|
+
:deletable?,
|
31
|
+
:default_project_version,
|
32
|
+
:to_s,
|
33
|
+
to: :@version
|
34
|
+
|
35
|
+
def initialize(version)
|
36
|
+
@version = version
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'liquid'
|
2
|
+
|
3
|
+
module Redmineup
|
4
|
+
module Liquid
|
5
|
+
module Filters
|
6
|
+
module Additional
|
7
|
+
def parse_inline_attachments(text, obj)
|
8
|
+
attachments = obj.attachments if obj.respond_to?(:attachments)
|
9
|
+
|
10
|
+
if attachments.present?
|
11
|
+
text.gsub!(/src="([^\/"]+\.(bmp|gif|jpg|jpe|jpeg|png))"(\s+alt="([^"]*)")?/i) do |m|
|
12
|
+
filename, ext, alt, alttext = $1, $2, $3, $4
|
13
|
+
# search for the picture in attachments
|
14
|
+
if found = Attachment.latest_attach(attachments, CGI.unescape(filename))
|
15
|
+
desc = found.description.to_s.delete('"')
|
16
|
+
alt = " title=\"#{desc}\" alt=\"#{desc}\"" if !desc.blank? && alttext.blank?
|
17
|
+
"src='data:image/#{found.content_type};base64,#{found.to_base64_string}' #{alt}"
|
18
|
+
else
|
19
|
+
m
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
text
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
::Liquid::Template.register_filter(Redmineup::Liquid::Filters::Additional)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -38,6 +38,16 @@ module Redmineup
|
|
38
38
|
Digest::MD5.hexdigest(input) unless input.blank?
|
39
39
|
end
|
40
40
|
|
41
|
+
def regex_replace(str, regex_search, value_replace)
|
42
|
+
regex = /#{regex_search}/
|
43
|
+
return str.gsub(regex, value_replace)
|
44
|
+
end
|
45
|
+
|
46
|
+
def regex_replace_once(str, regex_search, value_replace)
|
47
|
+
regex = /#{regex_search}/
|
48
|
+
return str.sub(regex, value_replace)
|
49
|
+
end
|
50
|
+
|
41
51
|
# example:
|
42
52
|
# {{ "http:://www.example.com?key=hello world" | encode }}
|
43
53
|
#
|
@@ -100,7 +110,12 @@ module Redmineup
|
|
100
110
|
def custom_field(input, field_name)
|
101
111
|
if input.respond_to?(:custom_field_values)
|
102
112
|
custom_value = input.custom_field_values.detect { |cfv| cfv.custom_field.name == field_name }
|
103
|
-
|
113
|
+
if custom_value
|
114
|
+
result = custom_value.custom_field.format.formatted_custom_value(nil, custom_value)
|
115
|
+
return result if result.respond_to?(:to_liquid)
|
116
|
+
drop_class = "Redmineup::Liquid::#{result.class.name}Drop"
|
117
|
+
Object.const_defined?(drop_class) ? drop_class.constantize.new(result) : result
|
118
|
+
end
|
104
119
|
end
|
105
120
|
end
|
106
121
|
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Redmineup
|
2
|
+
module Patches
|
3
|
+
module Compatibility
|
4
|
+
module SpritePatch
|
5
|
+
def self.included(base)
|
6
|
+
base.send(:include, InstanceMethods)
|
7
|
+
end
|
8
|
+
|
9
|
+
module InstanceMethods
|
10
|
+
def sprite_icon(icon_name, label = nil, icon_only: false, size: '18', css_class: nil, sprite: "icons", plugin: nil)
|
11
|
+
label
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
unless ActionView::Base.included_modules.include?(Redmineup::Patches::Compatibility::SpritePatch)
|
20
|
+
ActionView::Base.send(:include, Redmineup::Patches::Compatibility::SpritePatch)
|
21
|
+
end
|
@@ -4,4 +4,7 @@ end
|
|
4
4
|
if Redmine::VERSION.to_s < "5"
|
5
5
|
require 'redmineup/patches/compatibility/user_patch'
|
6
6
|
end
|
7
|
-
|
7
|
+
if Redmine::VERSION.to_s < "6"
|
8
|
+
require 'redmineup/patches/compatibility/sprite_patch'
|
9
|
+
end
|
10
|
+
require 'redmineup/patches/compatibility/routing_mapper_patch'
|
data/lib/redmineup/version.rb
CHANGED
data/lib/redmineup.rb
CHANGED
@@ -27,6 +27,7 @@ require 'redmineup/money_helper'
|
|
27
27
|
require 'redmineup/colors_helper'
|
28
28
|
|
29
29
|
require 'liquid'
|
30
|
+
require 'redmineup/liquid/filters/additional'
|
30
31
|
require 'redmineup/liquid/filters/base'
|
31
32
|
require 'redmineup/liquid/filters/arrays'
|
32
33
|
require 'redmineup/liquid/filters/colors'
|
@@ -37,16 +38,22 @@ require 'redmineup/liquid/drops/users_drop'
|
|
37
38
|
require 'redmineup/liquid/drops/time_entries_drop'
|
38
39
|
require 'redmineup/liquid/drops/attachment_drop'
|
39
40
|
require 'redmineup/liquid/drops/issue_relations_drop'
|
41
|
+
require 'redmineup/liquid/drops/version_drop'
|
42
|
+
require 'redmineup/liquid/drops/custom_field_enumeration_drop'
|
40
43
|
|
41
44
|
require 'redmineup/helpers/external_assets_helper'
|
42
45
|
require 'redmineup/helpers/form_tag_helper'
|
43
46
|
require 'redmineup/helpers/calendars_helper'
|
44
47
|
require 'redmineup/assets_manager'
|
45
48
|
|
46
|
-
require 'redmineup/patches/liquid_patch'
|
49
|
+
require 'redmineup/patches/liquid_patch'
|
47
50
|
|
48
51
|
module Redmineup
|
49
52
|
GEM_NAME = 'redmineup'.freeze
|
53
|
+
|
54
|
+
def self.plugin_installed?(plugin_id)
|
55
|
+
Rails.root.join("plugins/#{plugin_id}/init.rb").exist?
|
56
|
+
end
|
50
57
|
end
|
51
58
|
|
52
59
|
require 'application_record' unless defined?(ApplicationRecord)
|
@@ -57,7 +64,20 @@ if defined?(ActiveRecord::Base)
|
|
57
64
|
ActiveRecord::Base.extend(Redmineup::ActsAsVotable::Votable)
|
58
65
|
end
|
59
66
|
|
60
|
-
|
67
|
+
if defined?(Propshaft::Assembly)
|
68
|
+
Propshaft::Assembly.prepend(Module.new do
|
69
|
+
def initialize(config)
|
70
|
+
base_dir = Pathname.new(Redmineup::AssetsManager.base_path)
|
71
|
+
paths = Redmineup::AssetsManager.assets_paths.map { |path| Pathname.new(path)}
|
72
|
+
asset_prefix = "plugin_assets/#{Redmineup::GEM_NAME}"
|
73
|
+
|
74
|
+
config[:redmine_extension_paths] << Redmine::AssetPath.new(base_dir, paths, asset_prefix)
|
75
|
+
super
|
76
|
+
end
|
77
|
+
end)
|
78
|
+
else
|
79
|
+
Redmineup::AssetsManager.install_assets
|
80
|
+
end
|
61
81
|
|
62
82
|
if defined?(ActionView::Base)
|
63
83
|
ActionView::Base.send :include, Redmineup::CalendarsHelper
|
@@ -26,6 +26,11 @@ module Redmineup
|
|
26
26
|
assert_equal @user.name, @liquid_render.render('{{ issue.author.name }}')
|
27
27
|
end
|
28
28
|
|
29
|
+
def test_issue_attachments
|
30
|
+
attachment_author = @issue.attachments.first.author.name
|
31
|
+
assert_equal attachment_author, @liquid_render.render('{% assign attach = issue.attachments | first %}{{ attach.author.name }}')
|
32
|
+
end
|
33
|
+
|
29
34
|
def test_issue_delegated
|
30
35
|
assert_equal [@issue.id, @issue.subject, @issue.description].join('|'),
|
31
36
|
@liquid_render.render('{{ issue.id }}|{{ issue.subject }}|{{ issue.description }}')
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../liquid_helper'
|
2
|
+
include LiquidHelperMethods
|
3
|
+
|
4
|
+
module Redmineup
|
5
|
+
class AdditionalFilterTest < ActiveSupport::TestCase
|
6
|
+
def setup
|
7
|
+
@issue = Issue.first
|
8
|
+
@issue_drop = Liquid::IssueDrop.new(@issue)
|
9
|
+
@strainer = ::Liquid::Context.new.strainer
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_parse_inline_attachments
|
13
|
+
text = '<p>description with image <img src="screenshot.png" /></p>'
|
14
|
+
attachment = mock_attachment
|
15
|
+
attachments = [attachment]
|
16
|
+
@issue_drop.define_singleton_method(:attachments) { attachments }
|
17
|
+
Attachment.stub(:latest_attach, attachment) do
|
18
|
+
assert_equal '<p>description with image <img src="mock_url" title="attach_image" alt="attach_image" loading="lazy" /></p>', @strainer.parse_inline_attachments(text, @issue_drop)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def mock_attachment
|
25
|
+
attachment = Minitest::Mock.new
|
26
|
+
attachment.expect(:url, 'mock_url')
|
27
|
+
attachment.expect(:description, "attach_image")
|
28
|
+
attachment
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
data/test/models/attachment.rb
CHANGED
data/test/models/issue.rb
CHANGED
@@ -8,6 +8,7 @@ class Issue < ActiveRecord::Base
|
|
8
8
|
|
9
9
|
has_many :relations_from, class_name: 'IssueRelation', foreign_key: 'issue_from_id', dependent: :delete_all
|
10
10
|
has_many :relations_to, class_name: 'IssueRelation', foreign_key: 'issue_to_id', dependent: :delete_all
|
11
|
+
has_many :journals, foreign_key: 'journalized_id'
|
11
12
|
|
12
13
|
up_acts_as_draftable
|
13
14
|
up_acts_as_taggable
|
@@ -18,4 +19,8 @@ class Issue < ActiveRecord::Base
|
|
18
19
|
def visible?
|
19
20
|
true
|
20
21
|
end
|
22
|
+
|
23
|
+
def attachments
|
24
|
+
Attachment.all
|
25
|
+
end
|
21
26
|
end
|