redmine_crm 0.0.23 → 0.0.43
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/.gitignore +4 -1
- data/Gemfile +1 -1
- data/README.md +166 -33
- data/Rakefile +3 -12
- data/bitbucket-pipelines.yml +42 -0
- data/config/currency_iso.json +12 -0
- data/doc/CHANGELOG +81 -2
- data/lib/redmine_crm/acts_as_draftable/draft.rb +40 -0
- data/lib/redmine_crm/acts_as_draftable/rcrm_acts_as_draftable.rb +170 -0
- data/lib/redmine_crm/acts_as_list/list.rb +282 -0
- data/lib/redmine_crm/{rcrm_acts_as_taggable.rb → acts_as_taggable/rcrm_acts_as_taggable.rb} +105 -93
- data/lib/redmine_crm/acts_as_taggable/tag.rb +81 -0
- data/lib/redmine_crm/acts_as_taggable/tag_list.rb +111 -0
- data/lib/redmine_crm/acts_as_taggable/tagging.rb +16 -0
- data/lib/redmine_crm/acts_as_viewed/rcrm_acts_as_viewed.rb +274 -0
- data/lib/redmine_crm/{rcrm_acts_as_votable.rb → acts_as_votable/rcrm_acts_as_votable.rb} +15 -14
- data/lib/redmine_crm/acts_as_votable/rcrm_acts_as_voter.rb +20 -0
- data/lib/redmine_crm/{votable.rb → acts_as_votable/votable.rb} +54 -65
- data/lib/redmine_crm/{vote.rb → acts_as_votable/vote.rb} +6 -8
- data/lib/redmine_crm/{voter.rb → acts_as_votable/voter.rb} +29 -34
- data/lib/redmine_crm/assets_manager.rb +43 -0
- data/lib/redmine_crm/colors_helper.rb +192 -0
- data/lib/redmine_crm/compatibility/application_controller_patch.rb +33 -0
- data/lib/redmine_crm/currency/formatting.rb +0 -3
- data/lib/redmine_crm/currency/heuristics.rb +1 -1
- data/lib/redmine_crm/currency/loader.rb +5 -6
- data/lib/redmine_crm/helpers/external_assets_helper.rb +19 -0
- data/lib/redmine_crm/helpers/form_tag_helper.rb +76 -0
- data/lib/redmine_crm/helpers/tags_helper.rb +1 -3
- data/lib/redmine_crm/helpers/vote_helper.rb +29 -32
- data/lib/redmine_crm/liquid/drops/issues_drop.rb +191 -0
- data/lib/redmine_crm/liquid/drops/news_drop.rb +54 -0
- data/lib/redmine_crm/liquid/drops/projects_drop.rb +86 -0
- data/lib/redmine_crm/liquid/drops/time_entries_drop.rb +65 -0
- data/lib/redmine_crm/liquid/drops/users_drop.rb +68 -0
- data/lib/redmine_crm/liquid/filters/arrays.rb +187 -0
- data/lib/redmine_crm/liquid/filters/base.rb +217 -0
- data/lib/redmine_crm/liquid/filters/colors.rb +31 -0
- data/lib/redmine_crm/money_helper.rb +2 -4
- data/lib/redmine_crm/version.rb +1 -1
- data/lib/redmine_crm.rb +56 -21
- data/redmine_crm.gemspec +9 -4
- data/test/acts_as_draftable/draft_test.rb +29 -0
- data/test/acts_as_draftable/rcrm_acts_as_draftable_test.rb +179 -0
- data/test/{acts_as_taggable_test.rb → acts_as_taggable/rcrm_acts_as_taggable_test.rb} +117 -156
- data/test/acts_as_taggable/tag_list_test.rb +34 -0
- data/test/acts_as_taggable/tag_test.rb +72 -0
- data/test/acts_as_taggable/tagging_test.rb +15 -0
- data/test/{viewed_test.rb → acts_as_viewed/rcrm_acts_as_viewed_test.rb} +17 -15
- data/test/acts_as_votable/rcrm_acts_as_votable_test.rb +19 -0
- data/test/acts_as_votable/rcrm_acts_as_voter_test.rb +14 -0
- data/test/{votable_model_test.rb → acts_as_votable/votable_test.rb} +34 -5
- data/test/{voter_model_test.rb → acts_as_votable/voter_test.rb} +8 -8
- data/test/currency_test.rb +10 -10
- data/test/database.yml +14 -14
- data/test/fixtures/issues.yml +13 -1
- data/test/fixtures/news.yml +8 -0
- data/test/fixtures/projects.yml +10 -0
- data/test/fixtures/users.yml +6 -2
- data/test/liquid/drops/issues_drop_test.rb +34 -0
- data/test/liquid/drops/news_drop_test.rb +38 -0
- data/test/liquid/drops/projects_drop_test.rb +44 -0
- data/test/liquid/drops/uses_drop_test.rb +36 -0
- data/test/liquid/filters/arrays_filter_test.rb +31 -0
- data/test/liquid/filters/base_filter_test.rb +63 -0
- data/test/liquid/filters/colors_filter_test.rb +33 -0
- data/test/liquid/liquid_helper.rb +34 -0
- data/test/models/issue.rb +14 -0
- data/test/models/news.rb +3 -0
- data/test/models/project.rb +8 -0
- data/test/{fixtures → models}/user.rb +5 -1
- data/test/{fixtures → models}/vote_classes.rb +0 -21
- data/test/money_helper_test.rb +5 -5
- data/test/schema.rb +33 -10
- data/test/test_helper.rb +20 -72
- data/vendor/assets/images/vcard.png +0 -0
- data/vendor/assets/javascripts/Chart.bundle.min.js +16 -0
- data/vendor/assets/javascripts/select2.js +3 -0
- data/vendor/assets/javascripts/select2_helpers.js +186 -0
- data/vendor/assets/stylesheets/select2.css +414 -0
- metadata +162 -38
- data/lib/redmine_crm/rcrm_acts_as_viewed.rb +0 -287
- data/lib/redmine_crm/rcrm_acts_as_voter.rb +0 -27
- data/lib/redmine_crm/tag.rb +0 -81
- data/lib/redmine_crm/tag_list.rb +0 -112
- data/lib/redmine_crm/tagging.rb +0 -20
- data/test/fixtures/issue.rb +0 -14
- data/test/tag_test.rb +0 -64
- data/test/tagging_test.rb +0 -14
- data/test/votable_test.rb +0 -17
@@ -1,9 +1,7 @@
|
|
1
1
|
module RedmineCrm
|
2
2
|
module ActsAsVotable
|
3
3
|
module Voter
|
4
|
-
|
5
4
|
def self.included(base)
|
6
|
-
|
7
5
|
# allow user to define these
|
8
6
|
aliases = {
|
9
7
|
:vote_up_for => [:likes, :upvotes, :up_votes],
|
@@ -18,7 +16,6 @@ module RedmineCrm
|
|
18
16
|
}
|
19
17
|
|
20
18
|
base.class_eval do
|
21
|
-
|
22
19
|
has_many :votes, :class_name => 'RedmineCrm::ActsAsVotable::Vote', :as => :voter, :dependent => :destroy do
|
23
20
|
def votables
|
24
21
|
includes(:votable).map(&:votable)
|
@@ -30,107 +27,105 @@ module RedmineCrm
|
|
30
27
|
alias_method(new_method, method)
|
31
28
|
end
|
32
29
|
end
|
33
|
-
|
34
30
|
end
|
35
|
-
|
36
31
|
end
|
37
32
|
|
38
33
|
# voting
|
39
|
-
def vote
|
40
|
-
args[:votable].vote_by args.merge(
|
34
|
+
def vote(args)
|
35
|
+
args[:votable].vote_by args.merge(:voter => self)
|
41
36
|
end
|
42
37
|
|
43
|
-
def vote_up_for
|
38
|
+
def vote_up_for(model = nil, args = {})
|
44
39
|
vote :votable => model, :vote_scope => args[:vote_scope], :vote => true
|
45
40
|
end
|
46
41
|
|
47
|
-
def vote_down_for
|
42
|
+
def vote_down_for(model = nil, args = {})
|
48
43
|
vote :votable => model, :vote_scope => args[:vote_scope], :vote => false
|
49
44
|
end
|
50
45
|
|
51
|
-
def unvote_for
|
46
|
+
def unvote_for(model, args = {})
|
52
47
|
model.unvote :voter => self, :vote_scope => args[:vote_scope]
|
53
48
|
end
|
54
49
|
|
55
50
|
# results
|
56
|
-
def voted_on?
|
51
|
+
def voted_on?(votable, args = {})
|
57
52
|
votes = find_votes(:votable_id => votable.id, :votable_type => votable.class.base_class.name,
|
58
53
|
:vote_scope => args[:vote_scope])
|
59
|
-
votes.
|
54
|
+
!votes.empty?
|
60
55
|
end
|
61
56
|
|
62
|
-
def voted_up_on?
|
57
|
+
def voted_up_on?(votable, args = {})
|
63
58
|
votes = find_votes(:votable_id => votable.id, :votable_type => votable.class.base_class.name,
|
64
59
|
:vote_scope => args[:vote_scope], :vote_flag => true)
|
65
|
-
votes.
|
60
|
+
!votes.empty?
|
66
61
|
end
|
67
62
|
|
68
|
-
def voted_down_on?
|
63
|
+
def voted_down_on?(votable, args = {})
|
69
64
|
votes = find_votes(:votable_id => votable.id, :votable_type => votable.class.base_class.name,
|
70
65
|
:vote_scope => args[:vote_scope], :vote_flag => false)
|
71
|
-
votes.
|
66
|
+
!votes.empty?
|
72
67
|
end
|
73
68
|
|
74
|
-
def voted_as_when_voting_on
|
69
|
+
def voted_as_when_voting_on(votable, args = {})
|
75
70
|
vote = find_votes(:votable_id => votable.id, :votable_type => votable.class.base_class.name,
|
76
|
-
|
71
|
+
:vote_scope => args[:vote_scope]).select(:vote_flag).last
|
77
72
|
return nil unless vote
|
78
|
-
|
73
|
+
vote.vote_flag
|
79
74
|
end
|
80
75
|
|
81
|
-
def find_votes
|
76
|
+
def find_votes(extra_conditions = {})
|
82
77
|
votes.where(extra_conditions)
|
83
78
|
end
|
84
79
|
|
85
|
-
def find_up_votes
|
80
|
+
def find_up_votes(args = {})
|
86
81
|
find_votes :vote_flag => true, :vote_scope => args[:vote_scope]
|
87
82
|
end
|
88
83
|
|
89
|
-
def find_down_votes
|
84
|
+
def find_down_votes(args = {})
|
90
85
|
find_votes :vote_flag => false, :vote_scope => args[:vote_scope]
|
91
86
|
end
|
92
87
|
|
93
|
-
def find_votes_for_class
|
94
|
-
find_votes extra_conditions.merge(
|
88
|
+
def find_votes_for_class(klass, extra_conditions = {})
|
89
|
+
find_votes extra_conditions.merge(:votable_type => klass.name)
|
95
90
|
end
|
96
91
|
|
97
|
-
def find_up_votes_for_class
|
92
|
+
def find_up_votes_for_class(klass, args = {})
|
98
93
|
find_votes_for_class klass, :vote_flag => true, :vote_scope => args[:vote_scope]
|
99
94
|
end
|
100
95
|
|
101
|
-
def find_down_votes_for_class
|
96
|
+
def find_down_votes_for_class(klass, args = {})
|
102
97
|
find_votes_for_class klass, :vote_flag => false, :vote_scope => args[:vote_scope]
|
103
98
|
end
|
104
99
|
|
105
100
|
# Including polymporphic relations for eager loading
|
106
101
|
def include_objects
|
107
|
-
ActsAsVotable::Vote.includes(:votable)
|
102
|
+
RedmineCrm::ActsAsVotable::Vote.includes(:votable)
|
108
103
|
end
|
109
104
|
|
110
|
-
def find_voted_items
|
105
|
+
def find_voted_items(extra_conditions = {})
|
111
106
|
options = extra_conditions.merge :voter_id => id, :voter_type => self.class.base_class.name
|
112
107
|
include_objects.where(options).collect(&:votable)
|
113
108
|
end
|
114
109
|
|
115
|
-
def find_up_voted_items
|
110
|
+
def find_up_voted_items(extra_conditions = {})
|
116
111
|
find_voted_items extra_conditions.merge(:vote_flag => true)
|
117
112
|
end
|
118
113
|
|
119
|
-
def find_down_voted_items
|
114
|
+
def find_down_voted_items(extra_conditions = {})
|
120
115
|
find_voted_items extra_conditions.merge(:vote_flag => false)
|
121
116
|
end
|
122
117
|
|
123
|
-
def get_voted
|
118
|
+
def get_voted(klass, extra_conditions = {})
|
124
119
|
klass.joins(:votes_for).merge find_votes(extra_conditions)
|
125
120
|
end
|
126
121
|
|
127
|
-
def get_up_voted
|
122
|
+
def get_up_voted(klass)
|
128
123
|
klass.joins(:votes_for).merge find_up_votes
|
129
124
|
end
|
130
125
|
|
131
|
-
def get_down_voted
|
126
|
+
def get_down_voted(klass)
|
132
127
|
klass.joins(:votes_for).merge find_down_votes
|
133
128
|
end
|
134
129
|
end
|
135
130
|
end
|
136
|
-
end
|
131
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module RedmineCrm
|
2
|
+
class AssetsManager
|
3
|
+
def self.install_assets
|
4
|
+
return unless Gem.loaded_specs[GEM_NAME]
|
5
|
+
source = File.join(Gem.loaded_specs[GEM_NAME].full_gem_path, 'vendor', 'assets')
|
6
|
+
destination = File.join(Dir.pwd, 'public', 'plugin_assets', GEM_NAME)
|
7
|
+
return unless File.directory?(source)
|
8
|
+
|
9
|
+
source_files = Dir[source + '/**/*']
|
10
|
+
source_dirs = source_files.select { |d| File.directory?(d) }
|
11
|
+
source_files -= source_dirs
|
12
|
+
|
13
|
+
unless source_files.empty?
|
14
|
+
base_target_dir = File.join(destination, File.dirname(source_files.first).gsub(source, ''))
|
15
|
+
begin
|
16
|
+
FileUtils.mkdir_p(base_target_dir)
|
17
|
+
rescue Exception => e
|
18
|
+
raise "Could not create directory #{base_target_dir}: " + e.message
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
source_dirs.each do |dir|
|
23
|
+
target_dir = File.join(destination, dir.gsub(source, ''))
|
24
|
+
begin
|
25
|
+
FileUtils.mkdir_p(target_dir)
|
26
|
+
rescue Exception => e
|
27
|
+
raise "Could not create directory #{target_dir}: " + e.message
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
source_files.each do |file|
|
32
|
+
begin
|
33
|
+
target = File.join(destination, file.gsub(source, ''))
|
34
|
+
unless File.exist?(target) && FileUtils.identical?(file, target)
|
35
|
+
FileUtils.cp(file, target)
|
36
|
+
end
|
37
|
+
rescue Exception => e
|
38
|
+
raise "Could not copy #{file} to #{target}: " + e.message
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,192 @@
|
|
1
|
+
module RedmineCrm
|
2
|
+
class ColorsHelper
|
3
|
+
NAMED_COLORS = {
|
4
|
+
'aliceblue' => 'f0f8ff',
|
5
|
+
'antiquewhite' => 'faebd7',
|
6
|
+
'aqua' => '00ffff',
|
7
|
+
'aquamarine' => '7fffd4',
|
8
|
+
'azure' => 'f0ffff',
|
9
|
+
'beige' => 'f5f5dc',
|
10
|
+
'bisque' => 'ffe4c4',
|
11
|
+
'black' => '000',
|
12
|
+
'blanchedalmond' => 'ffebcd',
|
13
|
+
'blue' => '0000ff',
|
14
|
+
'blueviolet' => '8a2be2',
|
15
|
+
'brown' => 'a52a2a',
|
16
|
+
'burlywood' => 'deb887',
|
17
|
+
'burntsienna' => 'ea7e5d',
|
18
|
+
'cadetblue' => '5f9ea0',
|
19
|
+
'chartreuse' => '7fff00',
|
20
|
+
'chocolate' => 'd2691e',
|
21
|
+
'coral' => 'ff7f50',
|
22
|
+
'cornflowerblue' => '6495ed',
|
23
|
+
'cornsilk' => 'fff8dc',
|
24
|
+
'crimson' => 'dc143c',
|
25
|
+
'cyan' => '00ffff',
|
26
|
+
'darkblue' => '00008b',
|
27
|
+
'darkcyan' => '008b8b',
|
28
|
+
'darkgoldenrod' => 'b8860b',
|
29
|
+
'darkgray' => 'a9a9a9',
|
30
|
+
'darkgreen' => '006400',
|
31
|
+
'darkgrey' => 'a9a9a9',
|
32
|
+
'darkkhaki' => 'bdb76b',
|
33
|
+
'darkmagenta' => '8b008b',
|
34
|
+
'darkolivegreen' => '556b2f',
|
35
|
+
'darkorange' => 'ff8c00',
|
36
|
+
'darkorchid' => '9932cc',
|
37
|
+
'darkred' => '8b0000',
|
38
|
+
'darksalmon' => 'e9967a',
|
39
|
+
'darkseagreen' => '8fbc8f',
|
40
|
+
'darkslateblue' => '483d8b',
|
41
|
+
'darkslategray' => '2f4f4f',
|
42
|
+
'darkslategrey' => '2f4f4f',
|
43
|
+
'darkturquoise' => '00ced1',
|
44
|
+
'darkviolet' => '9400d3',
|
45
|
+
'deeppink' => 'ff1493',
|
46
|
+
'deepskyblue' => '00bfff',
|
47
|
+
'dimgray' => '696969',
|
48
|
+
'dimgrey' => '696969',
|
49
|
+
'dodgerblue' => '1e90ff',
|
50
|
+
'firebrick' => 'b22222',
|
51
|
+
'floralwhite' => 'fffaf0',
|
52
|
+
'forestgreen' => '228b22',
|
53
|
+
'fuchsia' => 'ff00ff',
|
54
|
+
'gainsboro' => 'dcdcdc',
|
55
|
+
'ghostwhite' => 'f8f8ff',
|
56
|
+
'gold' => 'ffd700',
|
57
|
+
'goldenrod' => 'daa520',
|
58
|
+
'gray' => '808080',
|
59
|
+
'green' => '008000',
|
60
|
+
'greenyellow' => 'adff2f',
|
61
|
+
'grey' => '808080',
|
62
|
+
'honeydew' => 'f0fff0',
|
63
|
+
'hotpink' => 'ff69b4',
|
64
|
+
'indianred' => 'cd5c5c',
|
65
|
+
'indigo' => '4b0082',
|
66
|
+
'ivory' => 'fffff0',
|
67
|
+
'khaki' => 'f0e68c',
|
68
|
+
'lavender' => 'e6e6fa',
|
69
|
+
'lavenderblush' => 'fff0f5',
|
70
|
+
'lawngreen' => '7cfc00',
|
71
|
+
'lemonchiffon' => 'fffacd',
|
72
|
+
'lightblue' => 'add8e6',
|
73
|
+
'lightcoral' => 'f08080',
|
74
|
+
'lightcyan' => 'e0ffff',
|
75
|
+
'lightgoldenrodyellow' => 'fafad2',
|
76
|
+
'lightgray' => 'd3d3d3',
|
77
|
+
'lightgreen' => '90ee90',
|
78
|
+
'lightgrey' => 'd3d3d3',
|
79
|
+
'lightpink' => 'ffb6c1',
|
80
|
+
'lightsalmon' => 'ffa07a',
|
81
|
+
'lightseagreen' => '20b2aa',
|
82
|
+
'lightskyblue' => '87cefa',
|
83
|
+
'lightslategray' => '789',
|
84
|
+
'lightslategrey' => '789',
|
85
|
+
'lightsteelblue' => 'b0c4de',
|
86
|
+
'lightyellow' => 'ffffe0',
|
87
|
+
'lime' => '00ff00',
|
88
|
+
'limegreen' => '32cd32',
|
89
|
+
'linen' => 'faf0e6',
|
90
|
+
'magenta' => 'ff00ff',
|
91
|
+
'maroon' => '800000',
|
92
|
+
'mediumaquamarine' => '66cdaa',
|
93
|
+
'mediumblue' => '0000cd',
|
94
|
+
'mediumorchid' => 'ba55d3',
|
95
|
+
'mediumpurple' => '9370db',
|
96
|
+
'mediumseagreen' => '3cb371',
|
97
|
+
'mediumslateblue' => '7b68ee',
|
98
|
+
'mediumspringgreen' => '00fa9a',
|
99
|
+
'mediumturquoise' => '48d1cc',
|
100
|
+
'mediumvioletred' => 'c71585',
|
101
|
+
'midnightblue' => '191970',
|
102
|
+
'mintcream' => 'f5fffa',
|
103
|
+
'mistyrose' => 'ffe4e1',
|
104
|
+
'moccasin' => 'ffe4b5',
|
105
|
+
'navajowhite' => 'ffdead',
|
106
|
+
'navy' => '000080',
|
107
|
+
'oldlace' => 'fdf5e6',
|
108
|
+
'olive' => '808000',
|
109
|
+
'olivedrab' => '6b8e23',
|
110
|
+
'orange' => 'ffa500',
|
111
|
+
'orangered' => 'ff4500',
|
112
|
+
'orchid' => 'da70d6',
|
113
|
+
'palegoldenrod' => 'eee8aa',
|
114
|
+
'palegreen' => '98fb98',
|
115
|
+
'paleturquoise' => 'afeeee',
|
116
|
+
'palevioletred' => 'db7093',
|
117
|
+
'papayawhip' => 'ffefd5',
|
118
|
+
'peachpuff' => 'ffdab9',
|
119
|
+
'peru' => 'cd853f',
|
120
|
+
'pink' => 'ffc0cb',
|
121
|
+
'plum' => 'dda0dd',
|
122
|
+
'powderblue' => 'b0e0e6',
|
123
|
+
'purple' => '800080',
|
124
|
+
'rebeccapurple' => '663399',
|
125
|
+
'red' => 'ff0000',
|
126
|
+
'rosybrown' => 'bc8f8f',
|
127
|
+
'royalblue' => '4169e1',
|
128
|
+
'saddlebrown' => '8b4513',
|
129
|
+
'salmon' => 'fa8072',
|
130
|
+
'sandybrown' => 'f4a460',
|
131
|
+
'seagreen' => '2e8b57',
|
132
|
+
'seashell' => 'fff5ee',
|
133
|
+
'sienna' => 'a0522d',
|
134
|
+
'silver' => 'c0c0c0',
|
135
|
+
'skyblue' => '87ceeb',
|
136
|
+
'slateblue' => '6a5acd',
|
137
|
+
'slategray' => '708090',
|
138
|
+
'slategrey' => '708090',
|
139
|
+
'snow' => 'fffafa',
|
140
|
+
'springgreen' => '00ff7f',
|
141
|
+
'steelblue' => '4682b4',
|
142
|
+
'tan' => 'd2b48c',
|
143
|
+
'teal' => '008080',
|
144
|
+
'thistle' => 'd8bfd8',
|
145
|
+
'tomato' => 'ff6347',
|
146
|
+
'turquoise' => '40e0d0',
|
147
|
+
'violet' => 'ee82ee',
|
148
|
+
'wheat' => 'f5deb3',
|
149
|
+
'white' => 'fff',
|
150
|
+
'whitesmoke' => 'f5f5f5',
|
151
|
+
'yellow' => 'ffff00',
|
152
|
+
'yellowgreen' => '9acd32'
|
153
|
+
}
|
154
|
+
|
155
|
+
class << self
|
156
|
+
# Amount should be a decimal between 0 and 1. Lower means darker
|
157
|
+
|
158
|
+
def darken_color(input_color, amount=0.4)
|
159
|
+
hex_color = hex_color(input_color).gsub('#','')
|
160
|
+
rgb = hex_color.scan(/../).map {|color| color.hex}
|
161
|
+
rgb[0] = (rgb[0].to_i * amount).round
|
162
|
+
rgb[1] = (rgb[1].to_i * amount).round
|
163
|
+
rgb[2] = (rgb[2].to_i * amount).round
|
164
|
+
"#%02x%02x%02x" % rgb
|
165
|
+
end
|
166
|
+
|
167
|
+
# Amount should be a decimal between 0 and 1. Higher lightgreen lighter
|
168
|
+
def lighten_color(input_color, amount=0.6)
|
169
|
+
hex_color = hex_color(input_color).gsub('#','')
|
170
|
+
rgb = hex_color.scan(/../).map {|color| color.hex}
|
171
|
+
rgb[0] = [(rgb[0].to_i + 255 * amount).round, 255].min
|
172
|
+
rgb[1] = [(rgb[1].to_i + 255 * amount).round, 255].min
|
173
|
+
rgb[2] = [(rgb[2].to_i + 255 * amount).round, 255].min
|
174
|
+
"#%02x%02x%02x" % rgb
|
175
|
+
end
|
176
|
+
|
177
|
+
def contrasting_text_color(input_color)
|
178
|
+
color = hex_color(input_color).gsub('#','')
|
179
|
+
convert_to_brightness_value(color) > 382.5 ? darken_color(color) : lighten_color(color)
|
180
|
+
end
|
181
|
+
|
182
|
+
def hex_color(input_color)
|
183
|
+
"##{NAMED_COLORS[input_color] || input_color}"
|
184
|
+
end
|
185
|
+
|
186
|
+
def convert_to_brightness_value(input_color)
|
187
|
+
(hex_color(input_color).scan(/../).map {|color| color.hex}).sum
|
188
|
+
end
|
189
|
+
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module RedmineCrm
|
2
|
+
module Patches
|
3
|
+
module ApplicationControllerPatch
|
4
|
+
def self.included(base) # :nodoc:
|
5
|
+
base.extend(ClassMethods)
|
6
|
+
base.class_eval do
|
7
|
+
unloadable # Send unloadable so it will not be unloaded in development
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
module ClassMethods
|
12
|
+
def before_action(*filters, &block)
|
13
|
+
before_filter(*filters, &block)
|
14
|
+
end
|
15
|
+
|
16
|
+
def after_action(*filters, &block)
|
17
|
+
after_filter(*filters, &block)
|
18
|
+
end
|
19
|
+
|
20
|
+
def skip_before_action(*filters, &block)
|
21
|
+
skip_before_filter(*filters, &block)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
unless ActionController::Base.methods.include?(:before_action)
|
29
|
+
ActionController::Base.send(
|
30
|
+
:include,
|
31
|
+
RedmineCrm::Patches::ApplicationControllerPatch
|
32
|
+
)
|
33
|
+
end
|
@@ -26,7 +26,6 @@ module RedmineCrm
|
|
26
26
|
alias_method name, method
|
27
27
|
end
|
28
28
|
|
29
|
-
|
30
29
|
def format(value, currency, *rules)
|
31
30
|
# support for old format parameters
|
32
31
|
rules = normalize_formatting_rules(rules)
|
@@ -220,8 +219,6 @@ module RedmineCrm
|
|
220
219
|
"\\1#{rules[:decimal_mark]}\\3")
|
221
220
|
end
|
222
221
|
end
|
223
|
-
|
224
222
|
end
|
225
|
-
|
226
223
|
end
|
227
224
|
end
|
@@ -3,13 +3,12 @@ module RedmineCrm
|
|
3
3
|
module Loader
|
4
4
|
DATA_PATH = File.expand_path("../../../../config", __FILE__)
|
5
5
|
|
6
|
-
# Loads and returns the currencies stored in JSON files
|
6
|
+
# Loads and returns the currencies stored in JSON files
|
7
|
+
# in the config directory.
|
7
8
|
#
|
8
9
|
# @return [Hash]
|
9
10
|
def load_currencies
|
10
|
-
|
11
|
-
# currencies.merge! parse_currency_file("currency_non_iso.json")
|
12
|
-
# currencies.merge! parse_currency_file("currency_backwards_compatible.json")
|
11
|
+
parse_currency_file('currency_iso.json')
|
13
12
|
end
|
14
13
|
|
15
14
|
private
|
@@ -17,8 +16,8 @@ module RedmineCrm
|
|
17
16
|
def parse_currency_file(filename)
|
18
17
|
json = File.read("#{DATA_PATH}/#{filename}")
|
19
18
|
json.force_encoding(::Encoding::UTF_8) if defined?(::Encoding)
|
20
|
-
JSON.parse(json, :
|
19
|
+
JSON.parse(json, symbolize_names: true)
|
21
20
|
end
|
22
21
|
end
|
23
22
|
end
|
24
|
-
end
|
23
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module RedmineCrm
|
2
|
+
module ExternalAssetsHelper
|
3
|
+
include ActionView::Helpers::JavaScriptHelper
|
4
|
+
|
5
|
+
def select2_assets
|
6
|
+
return if @select2_tag_included
|
7
|
+
@select2_tag_included = true
|
8
|
+
javascript_include_tag('select2', plugin: GEM_NAME) +
|
9
|
+
stylesheet_link_tag('select2', plugin: GEM_NAME) +
|
10
|
+
javascript_include_tag('select2_helpers', plugin: GEM_NAME)
|
11
|
+
end
|
12
|
+
|
13
|
+
def chartjs_assets
|
14
|
+
return if @chartjs_tag_included
|
15
|
+
@chartjs_tag_included = true
|
16
|
+
javascript_include_tag('Chart.bundle.min', plugin: GEM_NAME)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
module RedmineCrm
|
2
|
+
module FormTagHelper
|
3
|
+
# Allows include select2 into your views.
|
4
|
+
#
|
5
|
+
# ==== Examples
|
6
|
+
# select2_tag 'city_id', '<option value="1">Lisbon</option>...'
|
7
|
+
# select2_tag 'city_id', options_for_select(...)
|
8
|
+
# select2_tag 'tag_list', nil, :multiple => true, :data => [{ id: 0, text: 'deal' }, ...], :tags => true, :include_hidden => false %>
|
9
|
+
# select2_tag 'tag_list', options_for_select(...), :multiple => true, :style => 'width: 100%;', :url => '/tags', :placeholder => '+ add tag', :tags => true %>
|
10
|
+
#
|
11
|
+
# You may use select_tag options and additional options.
|
12
|
+
#
|
13
|
+
# ==== Additional options
|
14
|
+
# * <tt>:url</tt> Allows searches for remote data using the ajax.
|
15
|
+
# * <tt>:data</tt> Load dropdown options from a local array if +url+ option not set.
|
16
|
+
# * <tt>:placeholder</tt> Supports displaying a placeholder value.
|
17
|
+
# * <tt>:include_hidden</tt> Adds hidden field after select when +multiple+ option true. Default value true.
|
18
|
+
# * <tt>:allow_clear</tt> Provides support for clearable selections. Default value false.
|
19
|
+
# * <tt>:min_input_length</tt> Minimum number of characters required to start a search. Default value 0.
|
20
|
+
# * <tt>:format_state</tt> Defines template of search results in the drop-down.
|
21
|
+
# * <tt>:tags</tt> Used to enable tagging feature.
|
22
|
+
#
|
23
|
+
# <b>Note:</b> The HTML specification says when +multiple+ parameter passed to select and all options got deselected
|
24
|
+
# web browsers do not send any value to server.
|
25
|
+
#
|
26
|
+
# In case if you don't want the helper to generate this hidden field you can specify
|
27
|
+
# <tt>include_hidden: false</tt> option.
|
28
|
+
#
|
29
|
+
# <b>Note:</b> Select2 assets must be available on a page.
|
30
|
+
# To include select2 assets to a page, you need to use the helper select2_assets.
|
31
|
+
# For example:
|
32
|
+
# <% content_for :header_tags do %>
|
33
|
+
# <%= select2_assets %>
|
34
|
+
# <% end %>
|
35
|
+
#
|
36
|
+
# Also aliased as: select2
|
37
|
+
#
|
38
|
+
# select2 'city_id', options_for_select(...)
|
39
|
+
#
|
40
|
+
def select2_tag(name, option_tags = nil, options = {})
|
41
|
+
s = select_tag(name, option_tags, options)
|
42
|
+
|
43
|
+
if options[:multiple] && options.fetch(:include_hidden, true)
|
44
|
+
s << hidden_field_tag("#{name}[]", '')
|
45
|
+
end
|
46
|
+
|
47
|
+
s + javascript_tag("select2Tag('#{sanitize_to_id(name)}', #{options.to_json});")
|
48
|
+
end
|
49
|
+
|
50
|
+
alias select2 select2_tag
|
51
|
+
|
52
|
+
# Transforms select filters of +type+ fields into select2
|
53
|
+
#
|
54
|
+
# ==== Examples
|
55
|
+
# transform_to_select2 'tags', url: auto_complete_tags_url
|
56
|
+
# transform_to_select2 'people', format_state: 'formatStateWithAvatar', min_input_length: 1, url: '/managers'
|
57
|
+
#
|
58
|
+
# ==== Options
|
59
|
+
# * <tt>:url</tt> Defines URL to search remote data using the ajax.
|
60
|
+
# * <tt>:format_state</tt> Defines template of search results in the drop-down.
|
61
|
+
# * <tt>:min_input_length</tt> Minimum number of characters required to start a search. Default value 0.
|
62
|
+
# * <tt>:width</tt> Sets the width of the control. Default value '60%'.
|
63
|
+
# * <tt>:multiple</tt> Supports multi-value select box. If set to false the selection will not allow multiple choices. Default value true.
|
64
|
+
#
|
65
|
+
# <b>Note:</b> Select2 assets must be available on a page.
|
66
|
+
# To include select2 assets to a page, you need to use the helper select2_assets.
|
67
|
+
# For example:
|
68
|
+
# <% content_for :header_tags do %>
|
69
|
+
# <%= select2_assets %>
|
70
|
+
# <% end %>
|
71
|
+
#
|
72
|
+
def transform_to_select2(type, options = {})
|
73
|
+
javascript_tag("setSelect2Filter('#{type}', #{options.to_json});") unless type.empty?
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -3,13 +3,11 @@ module RedmineCrm
|
|
3
3
|
# See the README for an example using tag_cloud.
|
4
4
|
def tag_cloud(tags, classes)
|
5
5
|
return if tags.empty?
|
6
|
-
|
7
6
|
max_count = tags.sort_by(&:count).last.count.to_f
|
8
|
-
|
9
7
|
tags.each do |tag|
|
10
8
|
index = ((tag.count / max_count) * (classes.size - 1)).round
|
11
9
|
yield tag, classes[index]
|
12
10
|
end
|
13
11
|
end
|
14
12
|
end
|
15
|
-
end
|
13
|
+
end
|
@@ -1,38 +1,35 @@
|
|
1
1
|
module RedmineCrm
|
2
|
-
module ActsAsVotable
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
end
|
16
|
-
|
17
|
-
end
|
18
|
-
|
19
|
-
class VotableWords
|
20
|
-
|
21
|
-
def self.that_mean_true
|
22
|
-
['up', 'upvote', 'like', 'liked', 'positive', 'yes', 'good', 'true', 1, true]
|
2
|
+
module ActsAsVotable
|
3
|
+
module Helpers
|
4
|
+
# this helper provides methods that help find what words are
|
5
|
+
# up votes and what words are down votes
|
6
|
+
#
|
7
|
+
# It can be called
|
8
|
+
#
|
9
|
+
# votable_object.votable_words.that_mean_true
|
10
|
+
#
|
11
|
+
module Words
|
12
|
+
def votable_words
|
13
|
+
VotableWords
|
14
|
+
end
|
23
15
|
end
|
24
16
|
|
25
|
-
|
26
|
-
|
17
|
+
class VotableWords
|
18
|
+
def self.that_mean_true
|
19
|
+
['up', 'upvote', 'like', 'liked', 'positive', 'yes', 'good', 'true', 1, true]
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.that_mean_false
|
23
|
+
['down', 'downvote', 'dislike', 'disliked', 'negative', 'no', 'bad', 'false', 0, false]
|
24
|
+
end
|
25
|
+
|
26
|
+
# check is word is a true or bad vote
|
27
|
+
# if the word is unknown, then it counts it as a true/good
|
28
|
+
# vote. this exists to allow all voting to be good by default
|
29
|
+
def self.meaning_of(word)
|
30
|
+
!that_mean_false.include?(word)
|
31
|
+
end
|
27
32
|
end
|
28
|
-
|
29
|
-
# check is word is a true or bad vote
|
30
|
-
# if the word is unknown, then it counts it as a true/good
|
31
|
-
# vote. this exists to allow all voting to be good by default
|
32
|
-
def self.meaning_of word
|
33
|
-
!that_mean_false.include?(word)
|
34
|
-
end
|
35
|
-
|
36
33
|
end
|
37
34
|
end
|
38
|
-
end
|
35
|
+
end
|