redmineup 1.0.2 → 1.1.13

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 (83) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.gitlab-ci.yml +33 -0
  4. data/README.md +74 -153
  5. data/app/assets/images/icons.svg +8 -0
  6. data/app/assets/javascripts/chart.min.js +13 -0
  7. data/app/assets/javascripts/consumer.js +53 -0
  8. data/app/assets/javascripts/consumer_worker.js +219 -0
  9. data/app/assets/javascripts/jquery.colorPicker.min.js +26 -0
  10. data/{vendor → app}/assets/javascripts/select2_helpers.js +11 -1
  11. data/app/assets/stylesheets/calendars.css +15 -0
  12. data/{vendor → app}/assets/stylesheets/money.css +1 -2
  13. data/app/assets/stylesheets/redmineup.css +509 -0
  14. data/{vendor → app}/assets/stylesheets/select2.css +1 -1
  15. data/app/views/redmineup/_additional_assets.html.erb +1 -0
  16. data/config/locales/en.yml +1 -0
  17. data/config/locales/ru.yml +1 -0
  18. data/config/routes.rb +4 -0
  19. data/doc/CHANGELOG +116 -7
  20. data/doc/active-record-mixins.md +156 -0
  21. data/doc/assets-money-and-utilities.md +71 -0
  22. data/doc/tagging-and-select2.md +108 -0
  23. data/lib/action_cable/connection/rup_connection.rb +17 -0
  24. data/lib/action_cable/server/rup_configuration.rb +17 -0
  25. data/lib/action_cable/server/rup_server.rb +17 -0
  26. data/lib/application_record.rb +3 -0
  27. data/lib/redmineup/acts_as_taggable/tag.rb +26 -2
  28. data/lib/redmineup/acts_as_taggable/up_acts_as_taggable.rb +42 -2
  29. data/lib/redmineup/acts_as_viewed/up_acts_as_viewed.rb +1 -1
  30. data/lib/redmineup/acts_as_votable/votable.rb +2 -5
  31. data/lib/redmineup/assets_manager.rb +12 -1
  32. data/lib/redmineup/currency.rb +3 -1
  33. data/lib/redmineup/engine.rb +12 -2
  34. data/lib/redmineup/helpers/calendars_helper.rb +22 -0
  35. data/lib/redmineup/helpers/datetime_helper.rb +23 -0
  36. data/lib/redmineup/helpers/external_assets_helper.rb +15 -7
  37. data/lib/redmineup/helpers/form_tag_helper.rb +43 -0
  38. data/lib/redmineup/helpers/tags_helper.rb +41 -0
  39. data/lib/redmineup/helpers/test_helper.rb +12 -0
  40. data/lib/redmineup/hooks/views_layouts_hook.rb +1 -3
  41. data/lib/redmineup/liquid/drops/attachment_drop.rb +8 -0
  42. data/lib/redmineup/liquid/drops/custom_field_enumeration_drop.rb +11 -0
  43. data/lib/redmineup/liquid/drops/issues_drop.rb +11 -19
  44. data/lib/redmineup/liquid/drops/users_drop.rb +2 -3
  45. data/lib/redmineup/liquid/drops/version_drop.rb +38 -0
  46. data/lib/redmineup/liquid/filters/additional.rb +31 -0
  47. data/lib/redmineup/liquid/filters/base.rb +16 -1
  48. data/lib/redmineup/money_helper.rb +10 -2
  49. data/lib/redmineup/patches/action_cable_base_patch.rb +25 -0
  50. data/lib/redmineup/patches/action_cable_patch.rb +19 -0
  51. data/lib/redmineup/patches/auto_completes_controller_patch.rb +17 -0
  52. data/lib/redmineup/patches/compatibility/routing_mapper_patch.rb +16 -0
  53. data/lib/redmineup/patches/compatibility/sprite_patch.rb +13 -0
  54. data/lib/redmineup/patches/compatibility_patch.rb +4 -0
  55. data/lib/redmineup/patches/liquid_patch.rb +13 -22
  56. data/lib/redmineup/version.rb +1 -1
  57. data/lib/redmineup.rb +66 -40
  58. data/test/acts_as_taggable/rup_acts_as_taggable_test.rb +1 -1
  59. data/test/acts_as_taggable/tag_test.rb +16 -0
  60. data/test/acts_as_votable/votable_test.rb +9 -9
  61. data/test/acts_as_votable/voter_test.rb +4 -4
  62. data/test/database.yml +3 -3
  63. data/test/fixtures/tags.yml +2 -0
  64. data/test/liquid/drops/issues_drop_test.rb +5 -0
  65. data/test/liquid/filters/additional_filter_test.rb +35 -0
  66. data/test/liquid/filters/arrays_filter_test.rb +6 -3
  67. data/test/liquid/liquid_helper.rb +1 -1
  68. data/test/models/attachment.rb +5 -0
  69. data/test/models/issue.rb +5 -0
  70. data/test/models/journal.rb +6 -0
  71. data/test/schema.rb +65 -53
  72. data/test/tags_helper_view_test.rb +41 -0
  73. data/test/test_helper.rb +13 -12
  74. metadata +43 -14
  75. data/app/views/redmine_crm/redmineup_calendar/_calendar.html.erb +0 -34
  76. data/bitbucket-pipelines.yml +0 -50
  77. data/lib/redmineup/compatibility/application_controller_patch.rb +0 -33
  78. data/lib/redmineup/compatibility/routing_mapper_patch.rb +0 -25
  79. data/lib/redmineup/helpers/rup_calendar_helper.rb +0 -146
  80. /data/{vendor → app}/assets/images/money.png +0 -0
  81. /data/{vendor → app}/assets/images/vcard.png +0 -0
  82. /data/{vendor/assets/javascripts/Chart.bundle.min.js → app/assets/javascripts/Chart.bundle.min.js.bak} +0 -0
  83. /data/{vendor → app}/assets/javascripts/select2.js +0 -0
@@ -0,0 +1,156 @@
1
+ # ActiveRecord mixins
2
+
3
+ This page documents the model mixins provided by `redmineup`.
4
+
5
+ ## `up_acts_as_taggable`
6
+
7
+ Adds polymorphic tagging support.
8
+
9
+ ### Migration
10
+
11
+ ```ruby
12
+ class CreateTags < ActiveRecord::Migration[6.1]
13
+ def change
14
+ ActiveRecord::Base.create_taggable_table
15
+ end
16
+ end
17
+ ```
18
+
19
+ Useful migration helpers:
20
+
21
+ - `ActiveRecord::Base.create_taggable_table`
22
+ - `ActiveRecord::Base.drop_taggable_table`
23
+ - `ActiveRecord::Base.add_tags_column(:tags, name: :color, type: :integer, default: nil)`
24
+ - `ActiveRecord::Base.remove_tags_columns(:tags, :color)`
25
+
26
+ ### Model
27
+
28
+ ```ruby
29
+ class Product < ApplicationRecord
30
+ up_acts_as_taggable
31
+ end
32
+ ```
33
+
34
+ ### Runtime API
35
+
36
+ - `record.tag_list` / `record.tag_list = 'a, b'`
37
+ - `record.save_tags` (called via callbacks)
38
+ - `Model.available_tags(project: @project, limit: 30)`
39
+ - `Model.tagged_with(['tag1', 'tag2'])`
40
+ - `Model.find_related_tags('tag1')`
41
+
42
+ ## `up_acts_as_draftable`
43
+
44
+ Adds draft save/restore workflow for unsaved or changed records.
45
+
46
+ ### Migration
47
+
48
+ ```ruby
49
+ class CreateDrafts < ActiveRecord::Migration[6.1]
50
+ def change
51
+ ActiveRecord::Base.create_drafts_table
52
+ end
53
+ end
54
+ ```
55
+
56
+ Helpers:
57
+
58
+ - `ActiveRecord::Base.create_drafts_table`
59
+ - `ActiveRecord::Base.drop_drafts_table`
60
+
61
+ ### Model
62
+
63
+ ```ruby
64
+ class WikiPage < ApplicationRecord
65
+ up_acts_as_draftable parent: :wiki
66
+ end
67
+ ```
68
+
69
+ ### Runtime API
70
+
71
+ - `record.save_draft`
72
+ - `record.update_draft(attrs)`
73
+ - `Model.drafts(User.current)`
74
+ - `Model.from_draft(draft_or_id)`
75
+
76
+ ## `up_acts_as_viewed`
77
+
78
+ Tracks unique views by user (or IP) and total views.
79
+
80
+ ### Migration
81
+
82
+ ```ruby
83
+ class CreateViewings < ActiveRecord::Migration[6.1]
84
+ def change
85
+ ActiveRecord::Base.create_viewings_table
86
+ end
87
+ end
88
+ ```
89
+
90
+ Optional helpers for models with embedded counters:
91
+
92
+ - `ActiveRecord::Base.generate_viewings_columns(table)`
93
+ - `Model.add_viewings_columns`
94
+ - `Model.remove_viewings_columns`
95
+ - `ActiveRecord::Base.drop_viewings_table`
96
+
97
+ ### Model
98
+
99
+ ```ruby
100
+ class Question < ApplicationRecord
101
+ up_acts_as_viewed
102
+ end
103
+ ```
104
+
105
+ ### Runtime API
106
+
107
+ - `record.view(request.remote_ip, User.current)`
108
+ - `record.viewed?`
109
+ - `record.view_count`
110
+ - `record.viewed_by?(request.remote_ip, User.current)`
111
+
112
+ ## `up_acts_as_votable` and `up_acts_as_voter`
113
+
114
+ Adds voting capabilities for content and users.
115
+
116
+ ### Migration
117
+
118
+ ```ruby
119
+ class CreateVotes < ActiveRecord::Migration[6.1]
120
+ def change
121
+ ActiveRecord::Base.create_votable_table
122
+ end
123
+ end
124
+ ```
125
+
126
+ Helpers:
127
+
128
+ - `ActiveRecord::Base.create_votable_table`
129
+ - `ActiveRecord::Base.drop_votable_table`
130
+
131
+ ### Models
132
+
133
+ ```ruby
134
+ class Question < ApplicationRecord
135
+ up_acts_as_votable
136
+ end
137
+
138
+ class User < ApplicationRecord
139
+ up_acts_as_voter
140
+ end
141
+ ```
142
+
143
+ ## `up_acts_as_priceable`
144
+
145
+ Generates currency-aware `*_to_s` methods for price fields.
146
+
147
+ ### Model
148
+
149
+ ```ruby
150
+ class Deal < ApplicationRecord
151
+ up_acts_as_priceable :amount, :tax_amount, :subtotal, :total
152
+ end
153
+ ```
154
+
155
+ This creates methods like `amount_to_s`, `tax_amount_to_s`, etc., backed by
156
+ `Redmineup::MoneyHelper#object_price`.
@@ -0,0 +1,71 @@
1
+ # Assets, money, and shared utilities
2
+
3
+ ## Asset helpers
4
+
5
+ Provided by `Redmineup::ExternalAssetsHelper`:
6
+
7
+ - `redmineup_assets`
8
+ - `select2_assets` (alias for `redmineup_assets`)
9
+ - `chartjs_assets`
10
+
11
+ `redmineup_assets` includes:
12
+
13
+ - `consumer.js`
14
+ - `select2.js`
15
+ - `select2_helpers.js`
16
+ - `redmineup.css`
17
+
18
+ The gem also hooks `view_layouts_base_html_head` and renders
19
+ `redmineup/_additional_assets`, so shared assets can be included globally.
20
+ Helpers are idempotent and safe to call from plugin views.
21
+
22
+ ## Money and currency
23
+
24
+ ### Settings API
25
+
26
+ `Redmineup::Settings::Money` provides access to plugin-level money settings:
27
+
28
+ - `default_currency`
29
+ - `major_currencies`
30
+ - `default_tax`
31
+ - `tax_type`, `tax_exclusive?`
32
+ - `thousands_delimiter`
33
+ - `decimal_separator`
34
+ - `disable_taxes?`
35
+
36
+ ### Formatting helpers
37
+
38
+ `Redmineup::MoneyHelper` provides:
39
+
40
+ - `price_to_currency(price, currency, options = {})`
41
+ - `object_price(record, :price)`
42
+ - `prices_collection_by_currency(hash, hide_zeros: true)`
43
+ - `collection_for_currencies_select`
44
+ - `all_currencies`
45
+
46
+ `up_acts_as_priceable` uses these helpers to generate `*_to_s` methods on
47
+ models.
48
+
49
+ ### Admin UI
50
+
51
+ `Redmineup::Currency.add_admin_money_menu` registers a Redmine admin menu item
52
+ that points to:
53
+
54
+ - `redmineup_settings_path(:money)`
55
+
56
+ ## Misc shared helpers and patches
57
+
58
+ - `Redmineup::CalendarsHelper#calendar_day_css_classes`
59
+ - Liquid patch for numeric conversion in `Liquid::StandardFilters`
60
+ - Liquid filters/drops under `lib/redmineup/liquid/**`
61
+ - Compatibility and ActionCable patches loaded at gem boot
62
+
63
+ ## Version requirements in plugins
64
+
65
+ Plugins typically guard compatibility in `init.rb`:
66
+
67
+ ```ruby
68
+ requires_redmineup version_or_higher: '1.1.5'
69
+ ```
70
+
71
+ Use this to fail fast when a plugin depends on newer helper or API behavior.
@@ -0,0 +1,108 @@
1
+ # Tagging and Select2 helpers
2
+
3
+ This page covers view helpers used by RedmineUP plugins for tag rendering,
4
+ tag editing, and select2-backed filters.
5
+
6
+ ## Tag rendering helpers
7
+
8
+ Provided by `Redmineup::TagsHelper`:
9
+
10
+ - `tag_link(url, tag_name, options = {})`
11
+ - `tag_links(tag_list, options = {}) { |tag| url }`
12
+ - `tag_cloud(tags, classes) { |tag, css_class| ... }`
13
+ - `tag_cloud_links(tags, key, options = {})`
14
+ - `tag_separator`
15
+
16
+ ### Recommended pattern
17
+
18
+ Define `*_tag_url` per plugin context:
19
+
20
+ ```ruby
21
+ def contact_tag_url(tag_name, options = {})
22
+ {
23
+ controller: 'contacts',
24
+ action: 'index',
25
+ set_filter: 1,
26
+ fields: [:tags],
27
+ values: { tags: [tag_name] },
28
+ operators: { tags: '=' }
29
+ }.merge(options)
30
+ end
31
+ ```
32
+
33
+ Then render the cloud via convention:
34
+
35
+ ```erb
36
+ <%= tag_cloud_links(Contact.available_tags(project: @project), :contact) %>
37
+ ```
38
+
39
+ `tag_cloud_links(tags, :contact)` expects `contact_tag_url(name)` to be defined
40
+ in the current view context.
41
+
42
+ ## Tag editors with Select2
43
+
44
+ Provided by `Redmineup::FormTagHelper`:
45
+
46
+ - `select2_tag(name, option_tags = nil, options = {})`
47
+ - `select2(...)` (alias)
48
+ - `tag_list_field_tag(name, value = [], options = {})`
49
+ - `transform_to_select2(type, options = {})`
50
+
51
+ `ActionView::Helpers::FormBuilder` is extended with:
52
+
53
+ - `f.tag_list_field(:tag_list, **options)`
54
+
55
+ ### Example: form object API
56
+
57
+ ```erb
58
+ <%= f.tag_list_field :tag_list, width: '95%' %>
59
+ ```
60
+
61
+ ### Example: standalone field API
62
+
63
+ ```erb
64
+ <%= tag_list_field_tag 'product[tag_list]', @product.tag_list, taggable_type: 'Product' %>
65
+ ```
66
+
67
+ ### Example: query/filter select2 transform
68
+
69
+ ```erb
70
+ <%= transform_to_select2 'product_tags',
71
+ url: auto_complete_taggable_tags_path(project_id: @project, taggable_type: 'Product') %>
72
+ ```
73
+
74
+ ## Tag autocomplete endpoint
75
+
76
+ `redmineup` adds a shared route:
77
+
78
+ - `GET /auto_completes/taggable_tags`
79
+
80
+ Named route:
81
+
82
+ - `auto_complete_taggable_tags_path`
83
+
84
+ Typical params used by plugins:
85
+
86
+ - `taggable_type`: model class name (`Contact`, `Person`, `Product`, `Issue`, `DriveEntry`)
87
+ - `project_id`: optional project scope
88
+ - `limit`: optional result size limit
89
+
90
+ Response format:
91
+
92
+ ```json
93
+ [
94
+ { "id": "important", "text": "important" },
95
+ { "id": "vip", "text": "vip" }
96
+ ]
97
+ ```
98
+
99
+ ## Real plugin usage
100
+
101
+ Patterns above are used by:
102
+
103
+ - `redmine_contacts` (`contact_tag_url`, `contact_tag_links`, `f.tag_list_field`)
104
+ - `redmine_people` (`people_tag_url`, `tag_cloud_links`, bulk edit tag forms)
105
+ - `redmine_products` (`product_tag_url`, `tag_list_field_tag`, select2 filters)
106
+ - `redmine_questions` (`question_tag_url`, `tag_cloud_links`)
107
+ - `redmine_drive` (`drive_entry_tag_url`, select2 tag filters)
108
+ - `redmineup_tags` (issue tag forms with `tag_list_field_tag`)
@@ -0,0 +1,17 @@
1
+ module ActionCable
2
+ module Connection
3
+ class RupConnection < ActionCable::Connection::Base
4
+ identified_by :current_user
5
+
6
+ def connect
7
+ self.current_user = find_verified_user
8
+ end
9
+
10
+ private
11
+
12
+ def find_verified_user
13
+ User.find_by(id: @request.session[:user_id]) || User.anonymous
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,17 @@
1
+ module ActionCable
2
+ module Server
3
+ class RupConfiguration < ActionCable::Server::Configuration
4
+ def initialize(connection_klass: 'ActionCable::Connection::Base')
5
+ super()
6
+
7
+ @connection_class = -> { connection_klass.constantize }
8
+ @logger ||= ::Rails.logger
9
+ @disable_request_forgery_protection = true
10
+ end
11
+
12
+ def cable
13
+ @cable ||= ActionCable.server.config.cable || { 'adapter' => 'async' }.with_indifferent_access
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,17 @@
1
+ module ActionCable
2
+ module Server
3
+ class RupServer < ActionCable::Server::Base
4
+ attr_reader :config
5
+
6
+ def initialize(config: self.class.config)
7
+ @config = config
8
+ @mutex = Monitor.new
9
+ @remote_connections = @event_loop = @worker_pool = @pubsub = nil
10
+ end
11
+
12
+ def setup_heartbeat_timer
13
+ Rails.logger.info('Heartbeat timer disabled for RupServer')
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,3 @@
1
+ class ApplicationRecord < ActiveRecord::Base
2
+ self.abstract_class = true
3
+ end
@@ -1,3 +1,5 @@
1
+ require 'digest/md5'
2
+
1
3
  module Redmineup
2
4
  module ActsAsTaggable #:nodoc:
3
5
  class Tag < ActiveRecord::Base #:nodoc:
@@ -17,6 +19,10 @@ module Redmineup
17
19
  where("LOWER(name) LIKE LOWER(?)", name).first || create(:name => name)
18
20
  end
19
21
 
22
+ def self.color_from_name(str)
23
+ "##{Digest::MD5.hexdigest(str.to_s)[0..5]}"
24
+ end
25
+
20
26
  def ==(object)
21
27
  super || (object.is_a?(Tag) && name == object.name)
22
28
  end
@@ -29,6 +35,18 @@ module Redmineup
29
35
  read_attribute(:count).to_i
30
36
  end
31
37
 
38
+ def color
39
+ if self.class.column_names.include?('color')
40
+ db_color = read_attribute(:color)
41
+ return ('#' + "%06x" % db_color) unless db_color.nil?
42
+ end
43
+ self.class.color_from_name(name)
44
+ end
45
+
46
+ def color=(color)
47
+ write_attribute(:color, color.sub(/\A#/, '').hex) unless color.blank?
48
+ end
49
+
32
50
  class << self
33
51
  # Calculate the tag counts for all tags.
34
52
  # :start_at - Restrict the tags to those created after a certain time
@@ -66,9 +84,15 @@ module Redmineup
66
84
  having = 'COUNT(*) > 0'
67
85
  having = [having, at_least, at_most].compact.join(' AND ')
68
86
  group_by = "#{Tag.table_name}.id, #{Tag.table_name}.name"
69
- # group_by << " AND #{having}" unless having.blank?
87
+ group_by << ", #{Tag.table_name}.color" if column_names.include?('color')
88
+
89
+ select_condition = if self.column_names.include?('color')
90
+ "#{Tag.table_name}.id, #{Tag.table_name}.name, #{Tag.table_name}.color, COUNT(*) AS count"
91
+ else
92
+ "#{Tag.table_name}.id, #{Tag.table_name}.name, COUNT(*) AS count"
93
+ end
70
94
 
71
- { :select => "#{Tag.table_name}.id, #{Tag.table_name}.name, COUNT(*) AS count",
95
+ { :select => select_condition,
72
96
  :joins => joins.join(" "),
73
97
  :conditions => conditions,
74
98
  :group => group_by,
@@ -44,7 +44,7 @@ module Redmineup
44
44
 
45
45
  # Create the taggable tables
46
46
  # === Options hash:
47
- # * <tt>:table_name</tt> - use a table name other than viewings
47
+ # * <tt>:table_name</tt> - use a table name other than tags
48
48
  # To be used during migration, but can also be used in other places
49
49
  def create_taggable_table(options = {})
50
50
  tag_name_table = options[:tags] || :tags
@@ -52,6 +52,7 @@ module Redmineup
52
52
  if !self.connection.table_exists?(tag_name_table)
53
53
  self.connection.create_table(tag_name_table) do |t|
54
54
  t.column :name, :string
55
+ t.column :color, :integer
55
56
  end
56
57
  end
57
58
 
@@ -64,6 +65,7 @@ module Redmineup
64
65
  # You should make sure that the column created is
65
66
  # long enough to store the required class names.
66
67
  t.column :taggable_type, :string
68
+ t.column :color, :integer, default: nil
67
69
 
68
70
  t.column :created_at, :datetime
69
71
  end
@@ -73,6 +75,43 @@ module Redmineup
73
75
  end
74
76
  end
75
77
 
78
+ # Create the needed column for up_acts_as_taggable.
79
+ # To be used during migration, but can also be used in other places.
80
+ #
81
+ # Options :name, :type, :default are required
82
+ #
83
+ # By default, a :color: column will be created
84
+ #
85
+ # E.g.: ActiveRecord::Base.add_tags_column(:tags, name: :color, type: :string, default: nil)
86
+ def add_tags_column(table_name = :tags, **options)
87
+ tag_column = options
88
+ tag_column.assert_valid_keys(:name, :type, :default)
89
+
90
+ raise ArgumentError, "Table `#{table_name}' not found" unless self.connection.table_exists?(table_name)
91
+
92
+ self.table_name = table_name
93
+ return if connection.column_exists?(table_name, tag_column[:name])
94
+
95
+ connection.add_column table_name, tag_column[:name], tag_column[:type], default: tag_column[:default]
96
+ reset_column_information
97
+ end
98
+
99
+ # Remove the up_acts_as_taggable specific columns
100
+ # To be used during migration, but can also be used in other places
101
+ #
102
+ # E.g.: ActiveRecord::Base.remove_tags_columns(:tags, :color, :color1)
103
+ def remove_tags_columns(table_name = :tags, *columns)
104
+ raise ArgumentError, "Table `#{table_name}' not found" unless self.connection.table_exists?(table_name)
105
+ return if columns.blank?
106
+
107
+ columns.each do |column|
108
+ if self.connection.column_exists?(table_name, column)
109
+ self.connection.remove_column table_name, column
110
+ self.reset_column_information
111
+ end
112
+ end
113
+ end
114
+
76
115
  def drop_taggable_table(options = {})
77
116
  tag_name_table = options[:tags] || :tags
78
117
  if self.connection.table_exists?(tag_name_table)
@@ -103,13 +142,14 @@ module Redmineup
103
142
  scope = scope.where("#{table_name}.project_id IN (%s)", projects.map(&:id).join(','))
104
143
  end
105
144
 
106
- if options[:name_like]
145
+ if options[:name_like].present?
107
146
  scope = scope.where("LOWER(#{Tag.table_name}.name) LIKE LOWER(?)", "%#{options[:name_like]}%")
108
147
  end
109
148
 
110
149
  group_fields = ''
111
150
  group_fields << ", #{Tag.table_name}.created_at" if Tag.respond_to?(:created_at)
112
151
  group_fields << ", #{Tag.table_name}.updated_at" if Tag.respond_to?(:updated_at)
152
+ group_fields << ", #{Tag.table_name}.color" if Tag.column_names.include?('color')
113
153
 
114
154
  if base_class.respond_to?(:visible_condition)
115
155
  visible_condition = base_class.visible_condition(User.current)
@@ -235,7 +235,7 @@ module Redmineup
235
235
  t.column :viewer_id, :integer
236
236
  t.column :viewed_id, :integer
237
237
  t.column :viewed_type, :string
238
- t.column :ip, :string, :limit => '24'
238
+ t.column :ip, :string, :limit => '45'
239
239
  t.column :created_at, :datetime
240
240
  end
241
241
 
@@ -232,11 +232,8 @@ module Redmineup
232
232
  end
233
233
  end
234
234
  self.record_timestamps = false
235
- if (::ActiveRecord::VERSION::MAJOR == 3) && (::ActiveRecord::VERSION::MINOR != 0)
236
- self.assign_attributes(updates, :without_protection => true) && self.save if !updates.empty?
237
- else
238
- self.assign_attributes(updates) && self.save if !updates.empty?
239
- end
235
+
236
+ self.update(updates) if !updates.empty?
240
237
  end
241
238
 
242
239
  # results
@@ -2,7 +2,8 @@ module Redmineup
2
2
  class AssetsManager
3
3
  def self.install_assets
4
4
  return unless Gem.loaded_specs[GEM_NAME]
5
- source = File.join(Gem.loaded_specs[GEM_NAME].full_gem_path, 'vendor', 'assets')
5
+
6
+ source = File.join(Gem.loaded_specs[GEM_NAME].full_gem_path, 'app', 'assets')
6
7
  destination = File.join(Dir.pwd, 'public', 'plugin_assets', GEM_NAME)
7
8
  return unless File.directory?(source)
8
9
 
@@ -39,5 +40,15 @@ module Redmineup
39
40
  end
40
41
  end
41
42
  end
43
+
44
+ def self.base_path
45
+ Gem.loaded_specs[GEM_NAME].full_gem_path
46
+ end
47
+
48
+ def self.assets_paths
49
+ return [] unless Gem.loaded_specs[GEM_NAME]
50
+
51
+ Dir[File.join(base_path, 'app', 'assets', '*')]
52
+ end
42
53
  end
43
54
  end
@@ -40,7 +40,9 @@ module Redmineup
40
40
  Redmine::MenuManager.map(:admin_menu).push(:redmineup_money,
41
41
  { controller: 'redmineup', action: 'settings', id: 'money' },
42
42
  caption: :label_redmineup_money,
43
- html: { class: 'icon icon-redminecrm-money' })
43
+ html: { class: 'icon icon-redmineup-money' },
44
+ icon: 'money',
45
+ plugin: :redmineup)
44
46
 
45
47
  end
46
48
 
@@ -1,4 +1,14 @@
1
1
  module Redmineup
2
- class Engine < Rails::Engine
3
- end
2
+ class Engine < Rails::Engine
3
+ config.to_prepare do
4
+ next unless defined?(::Redmine::Plugin)
5
+
6
+ require 'auto_completes_controller'
7
+
8
+ require File.expand_path('../redmineup/patches/compatibility_patch', __dir__)
9
+ require File.expand_path('../redmineup/patches/auto_completes_controller_patch', __dir__)
10
+
11
+ require File.expand_path('../redmineup/hooks/views_layouts_hook', __dir__)
12
+ end
13
+ end
4
14
  end
@@ -0,0 +1,22 @@
1
+ module Redmineup
2
+ module CalendarsHelper
3
+
4
+ def calendar_day_css_classes(calendar, day)
5
+ css = day.month==calendar.month ? +'even' : +'odd'
6
+ css << " today" if User.current.today == day
7
+ css << " nwday" if non_working_week_days.include?(day.cwday)
8
+ css
9
+ end
10
+
11
+ def non_working_week_days
12
+ @non_working_week_days ||= begin
13
+ days = Setting.non_working_week_days
14
+ if days.is_a?(Array) && days.size < 7
15
+ days.map(&:to_i)
16
+ else
17
+ []
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,23 @@
1
+ module Redmineup
2
+ module DatetimeHelper
3
+ def convert_time_to_user_timezone(time)
4
+ if User.current.time_zone
5
+ time.in_time_zone(User.current.time_zone)
6
+ else
7
+ time.utc? ? time.localtime : time
8
+ end
9
+ end
10
+
11
+ def convert_time_to_utc(time = Time.now)
12
+ return Time.now.utc if time.blank?
13
+ return time.utc if time.respond_to?(:utc)
14
+
15
+ time_zone = User.current.time_zone
16
+ return Time.parse(time).utc unless time_zone
17
+
18
+ Time.use_zone(time_zone) do
19
+ Time.zone.parse(time).utc
20
+ end
21
+ end
22
+ end
23
+ end
@@ -2,19 +2,27 @@ module Redmineup
2
2
  module ExternalAssetsHelper
3
3
  include ActionView::Helpers::JavaScriptHelper
4
4
 
5
+ def redmineup_assets
6
+ return if @redmineup_assets_included
7
+
8
+ @redmineup_assets_included = true
9
+ javascript_include_tag('consumer', plugin: GEM_NAME) +
10
+ javascript_include_tag('select2', plugin: GEM_NAME) +
11
+ javascript_include_tag('select2_helpers', plugin: GEM_NAME) +
12
+ javascript_include_tag('jquery.colorPicker.min', plugin: GEM_NAME) +
13
+ stylesheet_link_tag('redmineup', plugin: GEM_NAME) +
14
+ javascript_tag("window.redmineupWorkerUrl = #{asset_path('consumer_worker.js', plugin: GEM_NAME).to_json};")
15
+ end
16
+
5
17
  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)
18
+ redmineup_assets
11
19
  end
12
20
 
13
21
  def chartjs_assets
14
22
  return if @chartjs_tag_included
23
+
15
24
  @chartjs_tag_included = true
16
- javascript_include_tag('Chart.bundle.min', plugin: GEM_NAME)
25
+ javascript_include_tag('chart.min', plugin: GEM_NAME)
17
26
  end
18
-
19
27
  end
20
28
  end