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.
Files changed (91) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +4 -1
  3. data/Gemfile +1 -1
  4. data/README.md +166 -33
  5. data/Rakefile +3 -12
  6. data/bitbucket-pipelines.yml +42 -0
  7. data/config/currency_iso.json +12 -0
  8. data/doc/CHANGELOG +81 -2
  9. data/lib/redmine_crm/acts_as_draftable/draft.rb +40 -0
  10. data/lib/redmine_crm/acts_as_draftable/rcrm_acts_as_draftable.rb +170 -0
  11. data/lib/redmine_crm/acts_as_list/list.rb +282 -0
  12. data/lib/redmine_crm/{rcrm_acts_as_taggable.rb → acts_as_taggable/rcrm_acts_as_taggable.rb} +105 -93
  13. data/lib/redmine_crm/acts_as_taggable/tag.rb +81 -0
  14. data/lib/redmine_crm/acts_as_taggable/tag_list.rb +111 -0
  15. data/lib/redmine_crm/acts_as_taggable/tagging.rb +16 -0
  16. data/lib/redmine_crm/acts_as_viewed/rcrm_acts_as_viewed.rb +274 -0
  17. data/lib/redmine_crm/{rcrm_acts_as_votable.rb → acts_as_votable/rcrm_acts_as_votable.rb} +15 -14
  18. data/lib/redmine_crm/acts_as_votable/rcrm_acts_as_voter.rb +20 -0
  19. data/lib/redmine_crm/{votable.rb → acts_as_votable/votable.rb} +54 -65
  20. data/lib/redmine_crm/{vote.rb → acts_as_votable/vote.rb} +6 -8
  21. data/lib/redmine_crm/{voter.rb → acts_as_votable/voter.rb} +29 -34
  22. data/lib/redmine_crm/assets_manager.rb +43 -0
  23. data/lib/redmine_crm/colors_helper.rb +192 -0
  24. data/lib/redmine_crm/compatibility/application_controller_patch.rb +33 -0
  25. data/lib/redmine_crm/currency/formatting.rb +0 -3
  26. data/lib/redmine_crm/currency/heuristics.rb +1 -1
  27. data/lib/redmine_crm/currency/loader.rb +5 -6
  28. data/lib/redmine_crm/helpers/external_assets_helper.rb +19 -0
  29. data/lib/redmine_crm/helpers/form_tag_helper.rb +76 -0
  30. data/lib/redmine_crm/helpers/tags_helper.rb +1 -3
  31. data/lib/redmine_crm/helpers/vote_helper.rb +29 -32
  32. data/lib/redmine_crm/liquid/drops/issues_drop.rb +191 -0
  33. data/lib/redmine_crm/liquid/drops/news_drop.rb +54 -0
  34. data/lib/redmine_crm/liquid/drops/projects_drop.rb +86 -0
  35. data/lib/redmine_crm/liquid/drops/time_entries_drop.rb +65 -0
  36. data/lib/redmine_crm/liquid/drops/users_drop.rb +68 -0
  37. data/lib/redmine_crm/liquid/filters/arrays.rb +187 -0
  38. data/lib/redmine_crm/liquid/filters/base.rb +217 -0
  39. data/lib/redmine_crm/liquid/filters/colors.rb +31 -0
  40. data/lib/redmine_crm/money_helper.rb +2 -4
  41. data/lib/redmine_crm/version.rb +1 -1
  42. data/lib/redmine_crm.rb +56 -21
  43. data/redmine_crm.gemspec +9 -4
  44. data/test/acts_as_draftable/draft_test.rb +29 -0
  45. data/test/acts_as_draftable/rcrm_acts_as_draftable_test.rb +179 -0
  46. data/test/{acts_as_taggable_test.rb → acts_as_taggable/rcrm_acts_as_taggable_test.rb} +117 -156
  47. data/test/acts_as_taggable/tag_list_test.rb +34 -0
  48. data/test/acts_as_taggable/tag_test.rb +72 -0
  49. data/test/acts_as_taggable/tagging_test.rb +15 -0
  50. data/test/{viewed_test.rb → acts_as_viewed/rcrm_acts_as_viewed_test.rb} +17 -15
  51. data/test/acts_as_votable/rcrm_acts_as_votable_test.rb +19 -0
  52. data/test/acts_as_votable/rcrm_acts_as_voter_test.rb +14 -0
  53. data/test/{votable_model_test.rb → acts_as_votable/votable_test.rb} +34 -5
  54. data/test/{voter_model_test.rb → acts_as_votable/voter_test.rb} +8 -8
  55. data/test/currency_test.rb +10 -10
  56. data/test/database.yml +14 -14
  57. data/test/fixtures/issues.yml +13 -1
  58. data/test/fixtures/news.yml +8 -0
  59. data/test/fixtures/projects.yml +10 -0
  60. data/test/fixtures/users.yml +6 -2
  61. data/test/liquid/drops/issues_drop_test.rb +34 -0
  62. data/test/liquid/drops/news_drop_test.rb +38 -0
  63. data/test/liquid/drops/projects_drop_test.rb +44 -0
  64. data/test/liquid/drops/uses_drop_test.rb +36 -0
  65. data/test/liquid/filters/arrays_filter_test.rb +31 -0
  66. data/test/liquid/filters/base_filter_test.rb +63 -0
  67. data/test/liquid/filters/colors_filter_test.rb +33 -0
  68. data/test/liquid/liquid_helper.rb +34 -0
  69. data/test/models/issue.rb +14 -0
  70. data/test/models/news.rb +3 -0
  71. data/test/models/project.rb +8 -0
  72. data/test/{fixtures → models}/user.rb +5 -1
  73. data/test/{fixtures → models}/vote_classes.rb +0 -21
  74. data/test/money_helper_test.rb +5 -5
  75. data/test/schema.rb +33 -10
  76. data/test/test_helper.rb +20 -72
  77. data/vendor/assets/images/vcard.png +0 -0
  78. data/vendor/assets/javascripts/Chart.bundle.min.js +16 -0
  79. data/vendor/assets/javascripts/select2.js +3 -0
  80. data/vendor/assets/javascripts/select2_helpers.js +186 -0
  81. data/vendor/assets/stylesheets/select2.css +414 -0
  82. metadata +162 -38
  83. data/lib/redmine_crm/rcrm_acts_as_viewed.rb +0 -287
  84. data/lib/redmine_crm/rcrm_acts_as_voter.rb +0 -27
  85. data/lib/redmine_crm/tag.rb +0 -81
  86. data/lib/redmine_crm/tag_list.rb +0 -112
  87. data/lib/redmine_crm/tagging.rb +0 -20
  88. data/test/fixtures/issue.rb +0 -14
  89. data/test/tag_test.rb +0 -64
  90. data/test/tagging_test.rb +0 -14
  91. data/test/votable_test.rb +0 -17
@@ -0,0 +1,191 @@
1
+ module RedmineCrm
2
+ module Liquid
3
+ class IssuesDrop < ::Liquid::Drop
4
+ def initialize(issues)
5
+ @issues = issues
6
+ end
7
+
8
+ def before_method(id)
9
+ issue = @issues.where(:id => id).first || Issue.new
10
+ IssueDrop.new issue
11
+ end
12
+
13
+ def all
14
+ @all ||= @issues.map do |issue|
15
+ IssueDrop.new issue
16
+ end
17
+ end
18
+
19
+ def visible
20
+ @visible ||= @all.select(&:visible?)
21
+ end
22
+
23
+ def each(&block)
24
+ all.each(&block)
25
+ end
26
+
27
+ def size
28
+ @issues.size
29
+ end
30
+ end
31
+
32
+ class IssueDrop < ::Liquid::Drop
33
+ include ActionView::Helpers::UrlHelper
34
+
35
+ delegate :id,
36
+ :subject,
37
+ :description,
38
+ :visible?,
39
+ :open?,
40
+ :start_date,
41
+ :due_date,
42
+ :overdue?,
43
+ :done_ratio,
44
+ :estimated_hours,
45
+ :spent_hours,
46
+ :total_spent_hours,
47
+ :total_estimated_hours,
48
+ :is_private?,
49
+ :closed_on,
50
+ :updated_on,
51
+ :created_on,
52
+ :to => :@issue
53
+
54
+ def initialize(issue)
55
+ @issue = issue
56
+ end
57
+
58
+ def link
59
+ link_to @issue.subject, self.url
60
+ end
61
+
62
+ def url
63
+ Rails.application.routes.url_helpers.issue_path(@issue)
64
+ end
65
+
66
+ def author
67
+ @user ||= UserDrop.new(@issue.author)
68
+ end
69
+
70
+ def assignee
71
+ @assignee ||= UserDrop.new(@issue.assigned_to)
72
+ end
73
+
74
+ def tracker
75
+ @tracker ||= @issue.tracker && @issue.tracker.name
76
+ end
77
+
78
+ def status
79
+ @status ||= @issue.status && @issue.status.name
80
+ end
81
+
82
+ def priority
83
+ @priority ||= @issue.priority && @issue.priority.name
84
+ end
85
+
86
+ def category
87
+ @category ||= @issue.category && @issue.category.name
88
+ end
89
+
90
+ def version
91
+ @version ||= @issue.fixed_version && @issue.fixed_version.name
92
+ end
93
+
94
+ def time_entries
95
+ @time_entries ||= TimeEntriesDrop.new @issue.time_entries
96
+ end
97
+
98
+ def parent
99
+ @parent ||= IssueDrop.new @issue.parent if @issue.parent
100
+ end
101
+
102
+ def project
103
+ @project ||= ProjectDrop.new @issue.project if @issue.project
104
+ end
105
+
106
+ def subtasks
107
+ @subtasks ||= IssuesDrop.new @issue.children
108
+ end
109
+
110
+ def notes
111
+ @notes ||= @issue.journals.where("#{Journal.table_name}.notes IS NOT ?", nil).order(:created_on).map(&:notes)
112
+ end
113
+
114
+ def journals
115
+ @journals ||= JournalsDrop.new @issue.journals.where("#{Journal.table_name}.notes IS NOT ?", nil)
116
+ end
117
+
118
+ def tags
119
+ @issue.respond_to?(:tag_list) && @issue.tag_list
120
+ end
121
+
122
+ def story_points
123
+ @issue.respond_to?(:story_points) && @issue.story_points
124
+ end
125
+
126
+ def color
127
+ @issue.respond_to?(:color) && @issue.color
128
+ end
129
+
130
+ def day_in_state
131
+ @issue.respond_to?(:day_in_state) && @issue.day_in_state
132
+ end
133
+
134
+ def checklists
135
+ @issue.respond_to?(:checklists) && @issue.checklists.map{|item| {"id_done" => item.is_done, "subject" => item.subject}}
136
+ end
137
+
138
+ def custom_field_values
139
+ @issue.custom_field_values
140
+ end
141
+
142
+ end
143
+
144
+ class JournalsDrop < ::Liquid::Drop
145
+ def initialize(journals)
146
+ @journals = journals
147
+ end
148
+
149
+ def all
150
+ @all ||= @journals.map do |journal|
151
+ JournalDrop.new journal
152
+ end
153
+ end
154
+
155
+ def visible
156
+ @visible ||= @all.select(&:visible?)
157
+ end
158
+
159
+ def each(&block)
160
+ all.each(&block)
161
+ end
162
+
163
+ def size
164
+ @journals.size
165
+ end
166
+ end
167
+
168
+ class JournalDrop < ::Liquid::Drop
169
+ delegate :id,
170
+ :notes,
171
+ :created_on,
172
+ :private_notes,
173
+ :to => :@journal,
174
+ allow_nil: true
175
+
176
+ def initialize(journal)
177
+ @journal = journal
178
+ end
179
+
180
+ def user
181
+ @user ||= UserDrop.new(@journal.user)
182
+ end
183
+
184
+ def issue
185
+ @issue ||= IssueDrop.new @journal.issue if @journal.issue
186
+ end
187
+
188
+ end
189
+
190
+ end
191
+ end
@@ -0,0 +1,54 @@
1
+ module RedmineCrm
2
+ module Liquid
3
+ class NewssDrop < ::Liquid::Drop
4
+
5
+ def self.default_drop
6
+ self.new News.visible.order("#{News.table_name}.created_on")
7
+ end
8
+
9
+ def initialize(newss)
10
+ @newss = newss
11
+ end
12
+
13
+ def before_method(id)
14
+ news = @newss.where(:id => id).first || News.new
15
+ NewsDrop.new news
16
+ end
17
+
18
+ def last
19
+ NewsDrop.new News.last
20
+ end
21
+
22
+ def all
23
+ @all ||= @newss.map do |news|
24
+ NewsDrop.new news
25
+ end
26
+ end
27
+
28
+ def each(&block)
29
+ all.each(&block)
30
+ end
31
+
32
+ def size
33
+ @newss.size
34
+ end
35
+ end
36
+
37
+ class NewsDrop < ::Liquid::Drop
38
+ delegate :id, :title, :summary, :description, :visible?, :commentable?, :to => :@news
39
+
40
+ def initialize(news)
41
+ @news = news
42
+ end
43
+
44
+ def author
45
+ UserDrop.new @news.author
46
+ end
47
+
48
+ def custom_field_values
49
+ @news.custom_field_values
50
+ end
51
+
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,86 @@
1
+ module RedmineCrm
2
+ module Liquid
3
+ class ProjectsDrop < ::Liquid::Drop
4
+
5
+ def self.default_drop
6
+ self.new Project.visible.order(:name)
7
+ end
8
+
9
+ def initialize(projects)
10
+ @projects = projects
11
+ end
12
+
13
+ def before_method(identifier)
14
+ project = @projects.where(:identifier => identifier).first || Project.new
15
+ ProjectDrop.new project
16
+ end
17
+
18
+ def all
19
+ @all ||= @projects.map do |project|
20
+ ProjectDrop.new project
21
+ end
22
+ end
23
+
24
+ def active
25
+ @active ||= @projects.select(&:active?).map do |project|
26
+ ProjectDrop.new project
27
+ end
28
+ end
29
+
30
+ def each(&block)
31
+ all.each(&block)
32
+ end
33
+
34
+ def size
35
+ @projects.size
36
+ end
37
+ end
38
+
39
+ class ProjectDrop < ::Liquid::Drop
40
+ include ActionView::Helpers::UrlHelper
41
+
42
+ delegate :id,
43
+ :identifier,
44
+ :name,
45
+ :is_public,
46
+ :description,
47
+ :visible?,
48
+ :active?,
49
+ :archived?,
50
+ :short_description,
51
+ :start_date,
52
+ :due_date,
53
+ :overdue?,
54
+ :completed_percent,
55
+ :to => :@project
56
+
57
+ def initialize(project)
58
+ @project = project
59
+ end
60
+
61
+ def link
62
+ link_to @project.name, self.url
63
+ end
64
+
65
+ def url
66
+ Rails.application.routes.url_helpers.project_path(@project)
67
+ end
68
+
69
+ def issues
70
+ @issues ||= IssuesDrop.new @project.issues
71
+ end
72
+
73
+ def users
74
+ @users ||= UsersDrop.new @project.users
75
+ end
76
+
77
+ def subprojects
78
+ @subprojects ||= ProjectsDrop.new @project.children
79
+ end
80
+
81
+ def custom_field_values
82
+ @project.custom_field_values
83
+ end
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,65 @@
1
+ module RedmineCrm
2
+ module Liquid
3
+ class TimeEntriesDrop < ::Liquid::Drop
4
+ def initialize(time_entries)
5
+ @time_entries = time_entries
6
+ end
7
+
8
+ def all
9
+ @all ||= @time_entries.map do |time_entry|
10
+ TimeEntryDrop.new time_entry
11
+ end
12
+ end
13
+
14
+ def visible
15
+ @visible ||= @all.select(&:visible?)
16
+ end
17
+
18
+ def each(&block)
19
+ all.each(&block)
20
+ end
21
+
22
+ def size
23
+ @time_entries.size
24
+ end
25
+ end
26
+
27
+ class TimeEntryDrop < ::Liquid::Drop
28
+ include ActionView::Helpers::UrlHelper
29
+
30
+ delegate :id,
31
+ :hours,
32
+ :comments,
33
+ :spent_on,
34
+ :tyear,
35
+ :tmonth,
36
+ :tweek,
37
+ :visible?,
38
+ :updated_on,
39
+ :created_on,
40
+ :to => :@time_entry,
41
+ allow_nil: true
42
+
43
+ def initialize(time_entry)
44
+ @time_entry = time_entry
45
+ end
46
+
47
+ def user
48
+ @user ||= UserDrop.new(@time_entry.user)
49
+ end
50
+
51
+ def issue
52
+ @issue ||= IssueDrop.new(@time_entry.issue)
53
+ end
54
+
55
+ def activity
56
+ @activity ||= @time_entry.activity && @time_entry.activity.name
57
+ end
58
+
59
+ def custom_field_values
60
+ @time_entry.custom_field_values
61
+ end
62
+
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,68 @@
1
+ module RedmineCrm
2
+ module Liquid
3
+ class UsersDrop < ::Liquid::Drop
4
+
5
+ def self.default_drop
6
+ self.new User.sorted
7
+ end
8
+
9
+ def initialize(users)
10
+ @users = users
11
+ end
12
+
13
+ def before_method(login)
14
+ user = @users.where(:login => login).first || User.new
15
+ UserDrop.new user
16
+ end
17
+
18
+ def current
19
+ UserDrop.new User.current
20
+ end
21
+
22
+ def all
23
+ @all ||= @users.map do |user|
24
+ UserDrop.new user
25
+ end
26
+ end
27
+
28
+ def each(&block)
29
+ all.each(&block)
30
+ end
31
+
32
+ def size
33
+ @users.size
34
+ end
35
+ end
36
+
37
+ class UserDrop < ::Liquid::Drop
38
+ delegate :id, :name, :firstname, :lastname, :mail, :active?, :admin?, :logged?, :language, :to => :@user, allow_nil: true
39
+
40
+ def initialize(user)
41
+ @user = user
42
+ end
43
+
44
+ def avatar
45
+ ApplicationController.helpers.avatar(@user)
46
+ end
47
+
48
+ def permissions
49
+ roles = @user.memberships.collect { |m| m.roles }.flatten.uniq
50
+ roles << (@user.logged? ? Role.non_member : Role.anonymous)
51
+ roles.map(&:permissions).flatten.uniq.map(&:to_s)
52
+ end
53
+
54
+ def groups
55
+ @user.groups.map(&:name)
56
+ end
57
+
58
+ def projects
59
+ ProjectsDrop.new @user.memberships.map(&:project).flatten.select(&:visible?).uniq
60
+ end
61
+
62
+ def custom_field_values
63
+ @user.custom_field_values
64
+ end
65
+
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,187 @@
1
+ module RedmineCrm
2
+ module Liquid
3
+ module Filters
4
+ module Arrays
5
+
6
+ # Get the first element of the passed in array
7
+ #
8
+ # Example:
9
+ # {{ product.images | first | to_img }}
10
+ #
11
+ # {{ product.images | first: 3 }}
12
+ #
13
+ def first(array, count=1)
14
+ (count > 1 ? array.first(count) : array.first) if array.respond_to?(:first)
15
+ end
16
+
17
+ # Convert the input into json string
18
+ #
19
+ # input - The Array or Hash to be converted
20
+ #
21
+ # Returns the converted json string
22
+ def jsonify(input)
23
+ as_liquid(input).to_json
24
+ end
25
+
26
+ # Group an array of items by a property
27
+ #
28
+ # input - the inputted Enumerable
29
+ # property - the property
30
+ #
31
+ # Returns an array of Hashes, each looking something like this:
32
+ # {"name" => "larry"
33
+ # "items" => [...] } # all the items where `property` == "larry"
34
+ def group_by(input, property)
35
+ if groupable?(input)
36
+ input.group_by { |item| item_property(item, property).to_s }.each_with_object([]) do |item, array|
37
+ array << {
38
+ "name" => item.first,
39
+ "items" => item.last,
40
+ "size" => item.last.size
41
+ }
42
+ end
43
+ else
44
+ input
45
+ end
46
+ end
47
+
48
+ # Filter an array of objects
49
+ #
50
+ # input - the object array
51
+ # property - property within each object to filter by
52
+ # value - desired value
53
+ #
54
+ # Returns the filtered array of objects
55
+ def where(input, property, value, operator='==')
56
+ return input unless input.respond_to?(:select)
57
+ input = input.values if input.is_a?(Hash)
58
+ if operator == '=='
59
+ input.select do |object|
60
+ Array(item_property(object, property)).map(&:to_s).include?(value.to_s)
61
+ end || []
62
+ elsif operator == '<>'
63
+ input.select do |object|
64
+ !Array(item_property(object, property)).map(&:to_s).include?(value.to_s)
65
+ end || []
66
+ elsif operator == '>'
67
+ input.select do |object|
68
+ item_property_value = item_property(object, property)
69
+ item_property_value && item_property_value > value
70
+ end || []
71
+ elsif operator == '<'
72
+ input.select do |object|
73
+ item_property_value = item_property(object, property)
74
+ item_property_value && item_property_value < value
75
+ end || []
76
+ elsif operator == 'match'
77
+ input.select do |object|
78
+ Array(item_property(object, property)).map(&:to_s).any?{|i| i.match(value.to_s)}
79
+ end || []
80
+ elsif operator == 'any'
81
+ input.select do |object|
82
+ item_property(object, property).present?
83
+ end || []
84
+ elsif operator == 'none'
85
+ input.select do |object|
86
+ item_property(object, property).blank?
87
+ end || []
88
+ else
89
+ []
90
+ end
91
+ end
92
+
93
+ # Filter an array of objects by tags
94
+ #
95
+ # input - the object array
96
+ # tags - quoted tags list divided by comma
97
+ # match - (all- defaut, any, exclude)
98
+ #
99
+ # Returns the filtered array of objects
100
+ def tagged_with(input, tags, match='all')
101
+ return input unless input.respond_to?(:select)
102
+ input = input.values if input.is_a?(Hash)
103
+ tag_list = input.is_a?(Array) ? tags.sort : tags.split(',').map(&:strip).sort
104
+ case match
105
+ when "all"
106
+ input.select do |object|
107
+ object.respond_to?(:tag_list) &&
108
+ (tag_list - Array(item_property(object, 'tag_list')).map(&:to_s).sort).empty?
109
+ end || []
110
+ when "any"
111
+ input.select do |object|
112
+ object.respond_to?(:tag_list) &&
113
+ (tag_list & Array(item_property(object, 'tag_list')).map(&:to_s).sort).any?
114
+ end || []
115
+ when "exclude"
116
+ input.select do |object|
117
+ object.respond_to?(:tag_list) &&
118
+ (tag_list & Array(item_property(object, 'tag_list')).map(&:to_s).sort).empty?
119
+ end || []
120
+ else
121
+ []
122
+ end
123
+ end
124
+
125
+ # Sort an array of objects
126
+ #
127
+ # input - the object array
128
+ # property - property within each object to filter by
129
+ # nils ('first' | 'last') - nils appear before or after non-nil values
130
+ #
131
+ # Returns the filtered array of objects
132
+ def sort(input, property = nil, nils = "first")
133
+ if input.nil?
134
+ raise ArgumentError, "Cannot sort a null object."
135
+ end
136
+ if property.nil?
137
+ input.sort
138
+ else
139
+ if nils == "first"
140
+ order = - 1
141
+ elsif nils == "last"
142
+ order = + 1
143
+ else
144
+ raise ArgumentError, "Invalid nils order: " \
145
+ "'#{nils}' is not a valid nils order. It must be 'first' or 'last'."
146
+ end
147
+
148
+ sort_input(input, property, order)
149
+ end
150
+ end
151
+
152
+ def pop(array, input = 1)
153
+ return array unless array.is_a?(Array)
154
+ new_ary = array.dup
155
+ new_ary.pop(input.to_i || 1)
156
+ new_ary
157
+ end
158
+
159
+ def push(array, input)
160
+ return array unless array.is_a?(Array)
161
+ new_ary = array.dup
162
+ new_ary.push(input)
163
+ new_ary
164
+ end
165
+
166
+ def shift(array, input = 1)
167
+ return array unless array.is_a?(Array)
168
+ new_ary = array.dup
169
+ new_ary.shift(input.to_i || 1)
170
+ new_ary
171
+ end
172
+
173
+ def unshift(array, input)
174
+ return array unless array.is_a?(Array)
175
+ new_ary = array.dup
176
+ new_ary.unshift(input)
177
+ new_ary
178
+ end
179
+
180
+ private
181
+
182
+ end # module ArrayFilters
183
+ end
184
+ end
185
+
186
+ ::Liquid::Template.register_filter(RedmineCrm::Liquid::Filters::Arrays)
187
+ end