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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: fb1379b61856d4d51a73e61eaa2af2505559bc55
4
- data.tar.gz: dac49994dbd3c6df0727e37b4f1739cdeeb11406
3
+ metadata.gz: c7ec4763f7c0ff9e5ce0744bcd8579020ed89b32
4
+ data.tar.gz: 45d4f87a91b17c299097c1f6aa5a9732507f30cc
5
5
  SHA512:
6
- metadata.gz: 58cad6ac2a7c193331c40f8e897a9462573e4ee126f623b727b5623ec437a921a4b2227aba13b91d625b8d77c1d95fbce24b4b4a580baa4864b1472cb2da38dd
7
- data.tar.gz: 70f067cf96642b3dab5d66469eef34daa4c52dd6dac4af215a2c026cc7c1e7551e8771299803c8db5cde6ffa6dd41b578f73a70bc7c89911b872a960114e80db
6
+ metadata.gz: a604d13c92a5a5e47ff93d7aeb3867c503c1743beb55059502cbc4fd28a2ddadac9e6cc7745e950249aace899dab2ed8e86d7ced27d5682bd2aade9f7c6e6ca3
7
+ data.tar.gz: d26992ff800bf72702248dd3d3afb416ee15a5432091ed38befa56196a436b75901fc04a328dc742450261d1e59dba55552e33c8a32e985e6834fada408c79dc
data/.gitignore CHANGED
@@ -4,6 +4,8 @@
4
4
  /_yardoc/
5
5
  /coverage/
6
6
  /pkg/
7
+ /public/
8
+ /test/public/
7
9
  /spec/reports/
8
10
  /tmp/
9
11
  *.bundle
@@ -15,4 +17,5 @@ mkmf.log
15
17
  /Guardfile
16
18
  /Gemfile.local
17
19
  /Gemfile.local.lock
18
- *.gem
20
+ *.gem
21
+ /.idea/*
data/Gemfile CHANGED
@@ -1,4 +1,4 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
3
  # Specify your gem's dependencies in redmine_crm.gemspec
4
- gemspec
4
+ gemspec
data/README.md CHANGED
@@ -1,71 +1,204 @@
1
1
  # RedmineCrm
2
2
 
3
- Gem include next functional for Redmine plugins:
4
- * rcrm_acts_as_taggable
5
- * rcrm_acts_as_votable
6
- * rcrm_acts_as_viewable
3
+ This gem is used at RedmineUP as a general place for shared functionality and
4
+ assets. It contains **Chart.js** and **select2** JS libraries, various mixins
5
+ for ActiveRecord models and other things you might find useful.
7
6
 
8
- ## Installation
7
+ Among mixins there are:
8
+
9
+ * rcrm_acts_as_draftable
10
+ * rcrm_acts_as_taggable
11
+ * rcrm_acts_as_viewable
12
+ * rcrm_acts_as_votable
9
13
 
10
- Add this line to your application's Gemfile:
11
14
 
15
+ ## Installation
16
+
17
+ Add it to your plugin's Gemfile:
12
18
  ```ruby
13
- gem "redmine_crm"
19
+ gem 'redmine_crm'
20
+ ```
21
+
22
+ Then invoke the following command in your plugin's or Redmine directory:
23
+ ```
24
+ $ bundle install
14
25
  ```
15
26
 
16
- And then execute:
27
+ And now you can start using it.
17
28
 
18
- $ bundle
19
29
 
20
- Or install it yourself as:
30
+ ## Usage
31
+ ### Drafts
32
+ This module allows saving and restoring drafts for different models. To be
33
+ saved as a draft, an instance does not need to pass the validations. Drafts
34
+ store not only model attributes, but also associations and virtual attributes.
35
+ A draft could be linked to a given user, so every user can manage his/her own
36
+ drafts independent of others. A draft might have a parent instance.
37
+
38
+ First of all, drafts need to be saved somewhere, so let's create a migration:
39
+ ```ruby
40
+ class CreateDrafts < Rails.version > '5' ? ActiveRecord::Migration[5.0] : ActiveRecord::Migration
41
+ def change
42
+ ActiveRecord::Base.create_drafts_table
43
+ end
44
+ end
45
+ ```
46
+
47
+ Then in the Redmine directory run:
48
+ ```
49
+ $ rake redmine:plugins:migrate
50
+ ```
21
51
 
22
- $ gem install redmine_crm
52
+ Next, add `rcrm_acts_as_draftable` to a model for which you want to save drafts:
53
+ ```ruby
54
+ class Message < ActiveRecord::Base
55
+ rcrm_acts_as_draftable
56
+ end
57
+ ```
23
58
 
24
- Create migration with next code:
59
+ And that's it for the preparation, now you're ready to make use of drafts:
25
60
  ```ruby
26
- require 'redmine_crm/rcrm_acts_as_taggable'
61
+ # You can save message as a draft.
62
+ Message.new(subject: 'foo').save_draft
63
+
64
+ # And later restore message from the draft.
65
+ draft = Message.drafts(nil).last
66
+ message = draft.restore
67
+ puts message.subject
68
+ # => foo
69
+
70
+ # Draft can be overwritten.
71
+ message.content = 'bar'
72
+ puts message.save_draft
73
+ # => true
74
+
75
+ # You can also save draft linked to a particular user.
76
+ Message.new(subject: 'baz').save_draft(user: current_user)
77
+
78
+ # And restore message from some user's draft.
79
+ user_draft = Message.drafts(current_user).last
80
+ user_message = user_draft.restore
81
+ puts user_message.subject
82
+ # => baz
83
+
84
+ # It's also possible to restore a bunch of messages at once.
85
+ messages = Message.drafts(current_user).restore_all
86
+ p messages.map(&:subject)
87
+ # => ["baz"]
88
+
89
+ # When a model instance is saved, corresponding draft is removed.
90
+ puts Message.drafts(current_user).count
91
+ # => 1
92
+ user_message.board_id = Board.first.id
93
+ user_message.save!
94
+ puts Message.drafts(current_user).count
95
+ # => 0
96
+
97
+ # Drafts will be saved only for new (not persisted) or changed instances.
98
+ puts Message.new.save_draft
99
+ # => true
100
+ persisted = Message.last
101
+ puts persisted.save_draft
102
+ # => false
103
+ persisted.subject = 'other subject'
104
+ puts persisted.save_draft
105
+ # => true
106
+ ```
27
107
 
28
- def self.up
29
- ActiveRecord::Base.create_taggable_table
30
- end
108
+ ### Tags
109
+ This module makes it possible to tag objects.
31
110
 
32
- def self.down
33
- ActiveRecord::Base.drop_taggable_table
111
+ First, create a migration:
112
+ ```ruby
113
+ class CreateTags < Rails.version > '5' ? ActiveRecord::Migration[5.0] : ActiveRecord::Migration
114
+ def change
115
+ ActiveRecord::Base.create_taggable_table
34
116
  end
35
117
  end
36
118
  ```
37
- For rcrm_acts_as_viewed You may add column for your model
38
- with help:
39
119
 
120
+ Then in the Redmine directory run:
121
+ ```
122
+ $ rake redmine:plugins:migrate
123
+ ```
124
+
125
+ Next, add `rcrm_acts_as_taggable` to a model for which you want to provide tags:
40
126
  ```ruby
127
+ class Contact < ActiveRecord::Base
128
+ rcrm_acts_as_taggable
129
+ end
130
+ ```
41
131
 
42
- YourModel.add_viewings_columns
132
+ TODO: Add examples of usage.
43
133
 
44
- ```
45
- It will add two column to your table (views and total_views)
46
- and view_count will return two number: total view and unique views.
47
- Without this migration you will get only unique views.
134
+ ### Viewings
135
+ This module allows you to count views for some ActiveRecord model instances.
48
136
 
49
- Run migration for plugin:
137
+ To count views you'll need to create a migration:
138
+ ```ruby
139
+ class CreateViewings < Rails.version > '5' ? ActiveRecord::Migration[5.0] : ActiveRecord::Migration
140
+ def change
141
+ ActiveRecord::Base.create_viewings_table
142
+ end
143
+ end
144
+ ```
50
145
 
146
+ To apply it, run the following in the Redmine directory:
51
147
  ```
52
- rake redmine:plugins:migrate
148
+ $ rake redmine:plugins:migrate
53
149
  ```
54
150
 
55
- ## Usage
151
+ Then add `rcrm_acts_as_viewed` to a model for which you want to count views.
152
+ ```ruby
153
+ class Question < ActiveRecord::Base
154
+ rcrm_acts_as_viewed
155
+ end
156
+ ```
157
+
158
+ TODO: Provide some usage examples.
56
159
 
57
- Add to model
160
+ ### Votes
161
+ With this module you can make your models votable and allow users to vote.
58
162
 
163
+ As always, create a migration first:
59
164
  ```ruby
60
- rcrm_acts_as_taggable
165
+ class CreateVotes < Rails.version > '5' ? ActiveRecord::Migration[5.0] : ActiveRecord::Migration
166
+ def change
167
+ ActiveRecord::Base.create_votable_table
168
+ end
169
+ end
170
+ ```
171
+
172
+ Then apply it by running the following command in the Redmine directory:
173
+ ```
174
+ $ rake redmine:plugins:migrate
175
+ ```
176
+
177
+ To make a model votable, add `rcrm_acts_as_votable` to it:
178
+ ```ruby
179
+ class Question < ActiveRecord::Base
61
180
  rcrm_acts_as_votable
181
+ end
62
182
  ```
63
183
 
184
+ TODO: Write about `rcrm_acts_as_voter` and add usage examples.
64
185
 
65
- ## Run test
66
186
 
187
+ ## Development
188
+
189
+ If you're planning to extend this gem, you will need to install development
190
+ dependencies. To do this, execute the following command in the project's
191
+ directory:
192
+ ```
193
+ $ bundle install
194
+ ```
195
+
196
+ After that you'll be able to run tests:
67
197
  ```
68
- rake test
198
+ $ bundle exec rake test
69
199
  ```
70
200
 
71
- For test for mysql set enviroment variable DB to value 'mysql' and run test.
201
+ SQLite in-memory database will be used by default, which is the fastest way to run tests. To run them using different database adapters, set `DB` environment variable to one of the available values — `mysql`, `postgresql`, `sqlite`. For example, to use PostgreSQL, invoke tests like so:
202
+ ```
203
+ $ bundle exec rake test DB=postgresql
204
+ ```
data/Rakefile CHANGED
@@ -1,20 +1,11 @@
1
- require "bundler/gem_tasks"
2
-
3
- require 'rake'
1
+ require 'bundler/gem_tasks'
4
2
  require 'rake/testtask'
5
- # require 'rake/rdoctask'
6
3
 
7
- desc 'Default: run unit tests.'
8
- task :default => :test
4
+ task default: :test
9
5
 
10
- desc 'Test the acts_as_taggable_on_steroids plugin.'
6
+ desc 'Run tests'
11
7
  Rake::TestTask.new(:test) do |t|
12
8
  t.libs << 'test'
13
9
  t.pattern = 'test/**/*_test.rb'
14
10
  t.verbose = true
15
11
  end
16
-
17
- desc 'Copy migration file to db/migration of plugin'
18
- file 'copy_migration' do
19
- cp "generators/redmine_crm_migration/templates/migration.rb", "db/migrate/redmine_crm.rb"
20
- end
@@ -0,0 +1,42 @@
1
+ # This is a sample build configuration for Ruby.
2
+ # Check our guides at https://confluence.atlassian.com/x/8r-5Mw for more examples.
3
+ # Only use spaces to indent your .yml configuration.
4
+ # -----
5
+ # You can specify a custom docker image from Docker Hub as your build environment.
6
+
7
+ pipelines:
8
+ default:
9
+ - step:
10
+ name: New ruby
11
+ image: ruby:2.4.1
12
+ script:
13
+ - sed -i "s/'sqlite3'/'sqlite3', '~> 1.3.6'/g" redmine_crm.gemspec
14
+ - sed -i "s/'mysql2'/'mysql2', '~> 0.4.0'/g" redmine_crm.gemspec
15
+ - bundle install
16
+ - bundle exec rake test DB=sqlite
17
+ - bundle exec rake test DB=postgresql
18
+ - bundle exec rake test DB=mysql
19
+ services:
20
+ - mysql
21
+ - postgres
22
+ - step:
23
+ name: Old ruby
24
+ image: ruby:1.9.3
25
+ script:
26
+ - bundle install
27
+ - bundle exec rake test DB=mysql
28
+ services:
29
+ - mysql
30
+
31
+ definitions:
32
+ services:
33
+ mysql:
34
+ image: mysql:5.7
35
+ environment:
36
+ MYSQL_DATABASE: redmine_crm_test
37
+ MYSQL_ROOT_PASSWORD: password
38
+ postgres:
39
+ image: postgres
40
+ environment:
41
+ POSTGRES_DB: redmine_crm_test
42
+ POSTGRES_PASSWORD: password
@@ -2528,5 +2528,17 @@
2528
2528
  "thousands_separator": ",",
2529
2529
  "iso_numeric": "967",
2530
2530
  "smallest_denomination": 5
2531
+ },
2532
+ "btc": {
2533
+ "priority": 100,
2534
+ "iso_code": "BTC",
2535
+ "name": "Bitcoin",
2536
+ "symbol": "฿",
2537
+ "subunit": "sat",
2538
+ "subunit_to_unit": 100000000,
2539
+ "symbol_first": false,
2540
+ "html_entity": "&#x0E3F;",
2541
+ "decimal_mark": ",",
2542
+ "thousands_separator": "."
2531
2543
  }
2532
2544
  }
data/doc/CHANGELOG CHANGED
@@ -1,8 +1,87 @@
1
1
  == Redmine CRM gem changelog
2
2
 
3
3
  Redmine crm gem - general functions for plugins (tags, vote, viewing, currency)
4
- Copyright (C) 2011-2016 RedmineUP
5
- http://www.redminecrm.com/
4
+ Copyright (C) 2011-2019 RedmineUP
5
+ https://www.redmineup.com/
6
+
7
+ == 2019-05-07 v0.0.43
8
+
9
+ * custom_field and custom_fields methods added from Reporter plugin
10
+ * Added support for multi email contacts select
11
+ * Fixed Safari select2 bug with moving screen
12
+ * Fixed after_action method compatibility
13
+
14
+ == 2018-10-24 v0.0.42
15
+
16
+ * Hotfix for ruby 1.9.3 hashes support
17
+ * Added ci image for ruby 1.9.3
18
+
19
+ == 2018-10-23 v0.0.41
20
+
21
+ * New fields for IssueDrop: tags, checklists, story_points, color, day_in_state
22
+ * New Liquid filters for managing colors: darken_color, lighten_color, contrasting_text_color, hex_color, convert_to_brightness_value
23
+ * Fixes for Drops to use nil delegates
24
+
25
+ == 2018-09-28 v0.0.40
26
+
27
+ * acts_as_draftable adopted for Redmine projects
28
+ * New Liquid Drops: JournalsDrop, JournalDrop, TimeEntriesDrop, TimeEntryDrop
29
+ * New liquid properties for issues: done_ratio, estimated_hours, spent_hours, total_spent_hours, total_estimated_hours, is_private, closed_on, assignee, tracker, status, priority, category, version, time_entries, parent, project, subtasks, notes, journals
30
+
31
+ == 2018-03-28 v0.0.37
32
+
33
+ * Added rcrm_acts_as_draftable
34
+ * Added transform_to_select2 helper
35
+
36
+ == 2018-03-22 v0.0.36
37
+
38
+ * select2_tag improvements
39
+
40
+ == 2018-03-05 v0.0.35
41
+
42
+ * Added select2_tag helper
43
+
44
+ == 2017-12-22 v0.0.34
45
+
46
+ * Fixed bug with Redmine 2.x support
47
+
48
+ == 2017-12-18 v0.0.33
49
+
50
+ * Rails 5 support (Redmine 4)
51
+ * rcrm_acts_as_list (Redmine 4)
52
+ * Bitcoin currency support
53
+
54
+ == 2017-07-18 v0.0.32
55
+
56
+ * charjs added
57
+
58
+ == 2017-05-29 v0.0.31
59
+
60
+ * select2 controls for contacts and people
61
+
62
+ == 2017-05-23 v0.0.30
63
+
64
+ * Fixed votable vote_by
65
+
66
+ == 2017-03-21 v0.0.29
67
+
68
+ * Updated available_tags method
69
+
70
+ == 2017-03-17 v0.0.28
71
+
72
+ * Fixed bug with quote_value
73
+
74
+ == 2017-03-13 v0.0.27
75
+
76
+ * Added IP to Votes
77
+
78
+ == 2017-03-10 v0.0.26
79
+
80
+ * Liquid gem with base filters and drops for Redmine objects
81
+
82
+ == 2016-11-12 v0.0.24
83
+
84
+ * Liquid gem dependency
6
85
 
7
86
  == 2016-09-28 v0.0.23
8
87
 
@@ -0,0 +1,40 @@
1
+ # The MIT License (MIT)
2
+
3
+ # Copyright (c) 2016-2018 Georg Ledermann
4
+
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the "Software"), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+
12
+ # The above copyright notice and this permission notice shall be included in
13
+ # all copies or substantial portions of the Software.
14
+
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ # THE SOFTWARE.
22
+
23
+ module RedmineCrm
24
+ module ActsAsDraftable
25
+ class Draft < ActiveRecord::Base
26
+ belongs_to :user
27
+ belongs_to :parent, polymorphic: true
28
+
29
+ validates_presence_of :data, :target_type
30
+
31
+ def restore
32
+ target_type.constantize.from_draft(self)
33
+ end
34
+
35
+ def self.restore_all
36
+ find_each.map(&:restore)
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,170 @@
1
+ # The MIT License (MIT)
2
+
3
+ # Copyright (c) 2016-2018 Georg Ledermann
4
+
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the "Software"), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+
12
+ # The above copyright notice and this permission notice shall be included in
13
+ # all copies or substantial portions of the Software.
14
+
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ # THE SOFTWARE.
22
+
23
+ module RedmineCrm
24
+ module ActsAsDraftable #:nodoc: all
25
+ module Base
26
+ ALLOWED_DRAFT_OPTIONS = [:parent]
27
+
28
+ def draftable?
29
+ false
30
+ end
31
+
32
+ def rcrm_acts_as_draftable(options = {})
33
+ raise ArgumentError unless options.is_a?(Hash)
34
+ raise ArgumentError unless options.keys.all? { |k| ALLOWED_DRAFT_OPTIONS.include?(k) }
35
+
36
+ class_attribute :draft_parent
37
+
38
+ if options[:parent]
39
+ parent_class = self.reflect_on_all_associations(:belongs_to).find { |a| a.name == options[:parent] }.try(:klass)
40
+ raise ArgumentError unless parent_class
41
+
42
+ unless parent_class.method_defined?(:drafts)
43
+ parent_class.class_eval do
44
+ def drafts(user)
45
+ Draft.where(user: user, parent: self)
46
+ end
47
+
48
+ def self.child_drafts(user)
49
+ Draft.where(user: user, parent_type: self.base_class.name)
50
+ end
51
+ end
52
+ end
53
+
54
+ self.draft_parent = options[:parent]
55
+ end
56
+
57
+ attr_accessor :draft_id
58
+ before_save :clear_draft
59
+
60
+ extend RedmineCrm::ActsAsDraftable::ClassMethods
61
+ include RedmineCrm::ActsAsDraftable::InstanceMethods
62
+ end
63
+ end # Base
64
+
65
+ module ClassMethods
66
+ def draftable?
67
+ true
68
+ end
69
+
70
+ def from_draft(draft_or_id)
71
+ draft = draft_or_id.is_a?(Draft) ? draft_or_id : Draft.find(draft_or_id)
72
+ raise ArgumentError unless draft.target_type == name
73
+
74
+ target = draft.target_type.constantize.new
75
+ target.load_from_draft(draft.data)
76
+
77
+ target.draft_id = draft.id
78
+ target
79
+ end
80
+
81
+ def drafts(user)
82
+ Draft.where(user_id: user.id, target_type: name)
83
+ end
84
+ end # ClassMethods
85
+
86
+ module InstanceMethods
87
+ def save_draft
88
+ return false unless new_record? || changed?
89
+
90
+ draft = self.draft || Draft.new
91
+ draft.data = dump_to_draft
92
+ draft.user_id = User.current.try(:id)
93
+ draft.parent = parent_for_draft
94
+ draft.target_type = self.class.name
95
+
96
+ result = draft.save
97
+ self.draft_id = draft.id if result
98
+ result
99
+ end
100
+
101
+ def update_draft(attributes)
102
+ with_transaction_returning_status do
103
+ assign_attributes(attributes)
104
+ save_draft
105
+ end
106
+ end
107
+
108
+ def draft
109
+ Draft.find_by_id(draft_id)
110
+ end
111
+
112
+ def parent_for_draft
113
+ return self if id.present?
114
+
115
+ self.class.draft_parent.present? ? send(self.class.draft_parent) : self
116
+ end
117
+
118
+ def last_draft
119
+ @last_draft ||= RedmineCrm::ActsAsDraftable::Draft.where(
120
+ target_type: self.class.name,
121
+ parent_type: parent_for_draft.try(:class).try(:to_s),
122
+ parent_id: parent_for_draft.try(:id),
123
+ user_id: User.current.try(:id)
124
+ ).last
125
+ end
126
+
127
+ def dump_to_draft
128
+ instance_values.default = nil
129
+ Marshal.dump(instance_values)
130
+ end
131
+
132
+ def load_from_draft(string)
133
+ values = Marshal.load(string)
134
+
135
+ values.each do |name, value|
136
+ instance_variable_set("@#{name}", value)
137
+ end
138
+ end
139
+
140
+ def clear_draft
141
+ if last_draft && last_draft.destroy
142
+ self.draft_id = nil
143
+ end
144
+ end
145
+ end # InstanceMethods
146
+
147
+ module Migration
148
+ def create_drafts_table
149
+ return if connection.table_exists?(:drafts)
150
+
151
+ connection.create_table :drafts do |t|
152
+ t.string :target_type, limit: 150, null: false
153
+ t.references :user
154
+ t.references :parent, polymorphic: true
155
+ t.binary :data, limit: 16777215, null: false
156
+ t.datetime :updated_at, null: false
157
+ end
158
+
159
+ connection.add_index :drafts, [:user_id, :target_type]
160
+ end
161
+
162
+ def drop_drafts_table
163
+ connection.drop_table :drafts if connection.table_exists?(:drafts)
164
+ end
165
+ end # Migration
166
+ end
167
+ end
168
+
169
+ ActiveRecord::Base.extend RedmineCrm::ActsAsDraftable::Base
170
+ ActiveRecord::Base.extend RedmineCrm::ActsAsDraftable::Migration