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
@@ -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
|