fat_free_crm 0.21.0 → 0.22.1

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.

Potentially problematic release.


This version of fat_free_crm might be problematic. Click here for more details.

Files changed (97) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/config/fat_free_crm.js +3 -0
  3. data/app/assets/config/manifest.js +1 -3
  4. data/app/assets/javascripts/application.js.erb +1 -0
  5. data/app/assets/javascripts/crm.js.coffee +6 -3
  6. data/app/assets/javascripts/crm_select2.js.coffee +4 -1
  7. data/app/assets/javascripts/crm_tags.js.coffee +4 -4
  8. data/app/assets/javascripts/crm_validations.js.coffee +12 -0
  9. data/app/assets/stylesheets/bootstrap-custom.scss +3 -3
  10. data/app/assets/stylesheets/common.scss +9 -0
  11. data/app/assets/stylesheets/rails.scss +1 -1
  12. data/app/controllers/admin/field_groups_controller.rb +0 -2
  13. data/app/controllers/admin/fields_controller.rb +16 -13
  14. data/app/controllers/admin/tags_controller.rb +1 -1
  15. data/app/controllers/admin/users_controller.rb +1 -1
  16. data/app/controllers/application_controller.rb +11 -0
  17. data/app/controllers/comments_controller.rb +2 -0
  18. data/app/controllers/emails_controller.rb +2 -0
  19. data/app/controllers/entities/accounts_controller.rb +3 -1
  20. data/app/controllers/entities/campaigns_controller.rb +3 -1
  21. data/app/controllers/entities/contacts_controller.rb +3 -1
  22. data/app/controllers/entities/leads_controller.rb +4 -2
  23. data/app/controllers/entities/opportunities_controller.rb +3 -1
  24. data/app/controllers/entities_controller.rb +3 -1
  25. data/app/controllers/home_controller.rb +2 -0
  26. data/app/controllers/lists_controller.rb +7 -4
  27. data/app/controllers/tasks_controller.rb +3 -1
  28. data/app/controllers/users_controller.rb +2 -0
  29. data/app/helpers/application_helper.rb +4 -4
  30. data/app/helpers/opportunities_helper.rb +4 -2
  31. data/app/helpers/users_helper.rb +1 -1
  32. data/app/models/entities/campaign.rb +2 -2
  33. data/app/models/fields/custom_field.rb +2 -2
  34. data/app/models/fields/custom_field_pair.rb +6 -7
  35. data/app/models/fields/field.rb +1 -3
  36. data/app/models/list.rb +1 -1
  37. data/app/models/observers/entity_observer.rb +1 -1
  38. data/app/models/polymorphic/comment.rb +1 -1
  39. data/app/models/setting.rb +4 -5
  40. data/app/models/users/user.rb +1 -1
  41. data/app/views/accounts/_contact_info.html.haml +1 -1
  42. data/app/views/accounts/_top_section.html.haml +3 -3
  43. data/app/views/accounts/show.js.haml +1 -1
  44. data/app/views/accounts/update.js.haml +1 -0
  45. data/app/views/admin/custom_fields/_check_boxes_field.html.haml +4 -1
  46. data/app/views/admin/custom_fields/_date_pair_field.html.haml +1 -1
  47. data/app/views/campaigns/_top_section.html.haml +2 -2
  48. data/app/views/campaigns/show.js.haml +1 -1
  49. data/app/views/campaigns/update.js.haml +1 -0
  50. data/app/views/contacts/_top_section.html.haml +5 -5
  51. data/app/views/contacts/show.js.haml +2 -3
  52. data/app/views/contacts/update.js.haml +1 -0
  53. data/app/views/devise/sessions/new.html.haml +4 -5
  54. data/app/views/fields/_group.html.haml +5 -2
  55. data/app/views/fields/_group_table.html.haml +4 -5
  56. data/app/views/fields/_group_view.html.haml +4 -1
  57. data/app/views/fields/_sidebar_show.html.haml +5 -8
  58. data/app/views/fields/group.js.erb +3 -1
  59. data/app/views/layouts/application.html.haml +1 -1
  60. data/app/views/leads/_top_section.html.haml +4 -4
  61. data/app/views/leads/show.js.haml +1 -1
  62. data/app/views/leads/update.js.haml +1 -0
  63. data/app/views/opportunities/_top_section.html.haml +2 -2
  64. data/app/views/opportunities/show.js.haml +1 -1
  65. data/app/views/opportunities/update.js.haml +1 -0
  66. data/app/views/shared/_add_comment.html.haml +1 -1
  67. data/app/views/shared/_address.html.haml +1 -1
  68. data/app/views/tasks/_top_section.html.haml +4 -4
  69. data/bin/bundle +108 -2
  70. data/bin/rails +3 -3
  71. data/bin/rake +2 -2
  72. data/bin/setup +12 -15
  73. data/config/application.rb +9 -4
  74. data/config/boot.rb +3 -5
  75. data/config/cable.yml +10 -0
  76. data/config/database.yml +25 -0
  77. data/config/environment.rb +4 -3
  78. data/config/environments/development.rb +47 -14
  79. data/config/environments/production.rb +17 -15
  80. data/config/environments/test.rb +19 -9
  81. data/config/initializers/action_mailer.rb +1 -0
  82. data/config/initializers/content_security_policy.rb +21 -26
  83. data/config/initializers/custom_field_ransack_translations.rb +2 -1
  84. data/config/initializers/filter_parameter_logging.rb +6 -2
  85. data/config/initializers/inflections.rb +4 -4
  86. data/config/initializers/permissions_policy.rb +12 -0
  87. data/config/settings.default.yml +2 -1
  88. data/config/storage.yml +5 -5
  89. data/db/demo/field_groups.yml +2 -1
  90. data/db/migrate/20230526212613_convert_to_active_storage.rb +27 -11
  91. data/db/schema.rb +107 -105
  92. data/lib/fat_free_crm/callback.rb +2 -3
  93. data/lib/fat_free_crm/custom_fields.rb +1 -0
  94. data/lib/fat_free_crm/mail_processor/dropbox.rb +1 -1
  95. data/lib/fat_free_crm/version.rb +2 -2
  96. metadata +43 -19
  97. data/config/initializers/new_framework_defaults_6_0.rb +0 -46
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7da1fdcaaf08df0256e5075214129f236ce36d156c24cd8ae4a282f3430b1ecb
4
- data.tar.gz: 255b0d61c8eb6d84bb94de174d075531c92c6b8dfb32826c6e304beafe578dc5
3
+ metadata.gz: 6ed29c049db6b4f6c31a7c1f5a3a083c1ab92a87d1475ae0808777a8d5c87c9d
4
+ data.tar.gz: ea1c36a463eafd3c50db26e072a447e74a8d07095a9bd19d0149096f681dc36b
5
5
  SHA512:
6
- metadata.gz: 95f133e5f454aad954dbb23ccb2da2fce1aa8dd5d6c9d9461b266b91f2339c4fed7db6b23ecb8199eab96473b183f5b492e55e60e0a60fcad574870e0f730ae2
7
- data.tar.gz: f83fa46bbe3312262e9845690c8f40ce7625a4b6f67ed018a1e39fab3b10af381fe0a305b7a585f52abdfd5638ba7526be704571e0096b94544296a3ed917f77
6
+ metadata.gz: 31bf4871e24767881af295b9b686a62b5c7de6d56894d67f13af38d6721f8728b36d3a0535b05a9b7f14ebbc65d5648ee4dd9924ded07fcf3308fe04835ea428
7
+ data.tar.gz: 71240615f55f2e0ac3527223d240470d28c3ada768a5f7240c4aea3a1b976ced11e10a2d4335cd733971ba526719f06ff44b7a15ca41127bb63aea7dbc5dd4ab
@@ -0,0 +1,3 @@
1
+ //= link_tree ../images
2
+ //= link_directory ../javascripts .js
3
+ //= link_directory ../stylesheets .css
@@ -1,3 +1 @@
1
- //= link_tree ../images
2
- //= link_directory ../javascripts .js
3
- //= link_directory ../stylesheets .css
1
+ //= link fat_free_crm
@@ -17,6 +17,7 @@
17
17
  //= require crm_loginout
18
18
  //= require crm_tags
19
19
  //= require crm_sortable
20
+ //= require crm_validations
20
21
  //= require textarea_autocomplete
21
22
  //= require crm_textarea_autocomplete
22
23
  //= require crm_select2
@@ -9,8 +9,8 @@
9
9
  @[0].toUpperCase() + @.substring(1)
10
10
 
11
11
  window.crm =
12
- EXPANDED: "▼"
13
- COLLAPSED: "►"
12
+ EXPANDED: "▽"
13
+ COLLAPSED: "▷"
14
14
  searchRequest: null
15
15
  autocompleter: null
16
16
  base_url: ""
@@ -336,7 +336,10 @@
336
336
 
337
337
  # Country dropdown needs special treatment ;-)
338
338
  country = $("#" + from + "_attributes_country").select2("data")
339
- $("#" + to + "_attributes_country").select2("data", country, true)
339
+ if country.length == 1
340
+ country_dropdown = $("#" + to + "_attributes_country")
341
+ country_dropdown.val(country[0].id)
342
+ country_dropdown.trigger('change')
340
343
 
341
344
 
342
345
  #----------------------------------------------------------------------------
@@ -15,6 +15,7 @@
15
15
  $(this).select2
16
16
  'width':'resolve'
17
17
  placeholder: $(this).attr("placeholder")
18
+ allowClear: true
18
19
  ajax:
19
20
  url: $(this).data("url")
20
21
  dataType: 'json'
@@ -22,16 +23,18 @@
22
23
  $(this).select2
23
24
  'width':'resolve'
24
25
  placeholder: $(this).attr("placeholder")
26
+ allowClear: true
25
27
 
26
28
  if $(this).prop("disabled") == true
27
29
  $(this).next('.select2-container').disable()
28
- $(this).next('.select2-container').hide()
30
+ $(this).next('.select2-container').hide()
29
31
 
30
32
  $(".select2_tag").not(".select2-container, .select2-offscreen").each ->
31
33
  $(this).select2
32
34
  'width':'resolve'
33
35
  placeholder: $(this).data("placeholder")
34
36
  multiple: $(this).data("multiple")
37
+ allowClear: true
35
38
 
36
39
  $(document).ready ->
37
40
  crm.make_select2()
@@ -7,16 +7,16 @@
7
7
 
8
8
  # The multiselect tag list has listeners to load/remove fieldsets related to tags
9
9
  #----------------------------------------------------------------------------
10
- $(document).on 'select2-selecting', "[name*='tag_list']", (event) ->
10
+ $(document).on 'select2:select', "[name*='tag_list']", (event) ->
11
11
  url = $(this).data('url')
12
12
  asset_id = $(this).data('asset-id')
13
13
  $.get(url, {
14
- tag: event.val
14
+ tag: event.params.data.text
15
15
  asset_id: asset_id
16
16
  collapsed: "no"
17
17
  })
18
18
 
19
- $(document).on 'select2-removing', "[name*='tag_list']", (event) ->
20
- $("#field_groups div[data-tag='" + event.val + "']").remove()
19
+ $(document).on 'select2:unselect', "[name*='tag_list']", (event) ->
20
+ $("#field_groups div[data-tag='" + event.params.data.text + "']").remove()
21
21
 
22
22
  ) jQuery
@@ -0,0 +1,12 @@
1
+ #------------------------------------------------------------------------------
2
+ (($) ->
3
+
4
+ # Ensure that any html5 required fields are unhidden when invalid
5
+ #----------------------------------------------------------------------------
6
+ $(document).on 'click', 'form.simple_form input:submit', (event) ->
7
+ form = this.closest('form')
8
+ invalidInputs = form.querySelectorAll(':invalid')
9
+ $(invalidInputs).each ->
10
+ $(this).closest('.field_group').show()
11
+
12
+ ) jQuery
@@ -165,12 +165,12 @@ button.navbar-toggler:focus {
165
165
  dl {
166
166
  li {
167
167
  width: 23%;
168
- font-size: 2rem;
168
+ font-size: 1rem;
169
169
  dt {
170
- font-size: inherit;
170
+ font-size: 0.75rem;
171
171
  }
172
172
  tt {
173
- font-size: inherit;
173
+ font-size: 0.75rem;
174
174
  }
175
175
  }
176
176
  }
@@ -784,3 +784,12 @@ table.asset_attributes {
784
784
  background-color: darkgray;
785
785
  }
786
786
  }
787
+
788
+
789
+ // Login page
790
+ .form-group.user_remember_me {
791
+ border: 0px;
792
+ .form-check {
793
+ padding-left: 6px;
794
+ }
795
+ }
@@ -28,7 +28,7 @@
28
28
  padding: 0px; } } }
29
29
 
30
30
  .fieldWithErrors {
31
- input {
31
+ input, select {
32
32
  border: {
33
33
  bottom: 1px solid lightpink;
34
34
  right: 1px solid lightpink; };
@@ -22,8 +22,6 @@ class Admin::FieldGroupsController < Admin::ApplicationController
22
22
  def edit
23
23
  @field_group = FieldGroup.find(params[:id])
24
24
 
25
- @previous = FieldGroup.find_by_id(Regexp.last_match[1]) || Regexp.last_match[1].to_i if params[:previous].to_s =~ /(\d+)\z/
26
-
27
25
  respond_with(@field_group)
28
26
  end
29
27
 
@@ -33,7 +33,7 @@ class Admin::FieldsController < Admin::ApplicationController
33
33
  # GET /fields/1/edit AJAX
34
34
  #----------------------------------------------------------------------------
35
35
  def edit
36
- @field = Field.find(params[:id])
36
+ @field = Field.find(params["id"])
37
37
  respond_with(@field)
38
38
  end
39
39
 
@@ -41,12 +41,12 @@ class Admin::FieldsController < Admin::ApplicationController
41
41
  # POST /fields.xml AJAX
42
42
  #----------------------------------------------------------------------------
43
43
  def create
44
- as = field_params[:as]
44
+ as = field_params["as"]
45
+ klass= Field.lookup_class(as).safe_constantize
45
46
  @field =
46
47
  if as.match?(/pair/)
47
- CustomFieldPair.create_pair(params).first
48
+ klass.create_pair("pair" => pair_params, "field" => field_params).first
48
49
  elsif as.present?
49
- klass = find_class(Field.lookup_class(as))
50
50
  klass.create(field_params)
51
51
  else
52
52
  Field.new(field_params).tap(&:valid?)
@@ -59,10 +59,10 @@ class Admin::FieldsController < Admin::ApplicationController
59
59
  # PUT /fields/1.xml AJAX
60
60
  #----------------------------------------------------------------------------
61
61
  def update
62
- if field_params[:as].match?(/pair/)
63
- @field = CustomFieldPair.update_pair(params).first
62
+ if field_params["as"].match?(/pair/)
63
+ @field = CustomFieldPair.update_pair("pair" => pair_params, "field" => field_params).first
64
64
  else
65
- @field = Field.find(params[:id])
65
+ @field = Field.find(params["id"])
66
66
  @field.update(field_params)
67
67
  end
68
68
 
@@ -73,7 +73,7 @@ class Admin::FieldsController < Admin::ApplicationController
73
73
  # DELETE /fields/1.xml HTML and AJAX
74
74
  #----------------------------------------------------------------------------
75
75
  def destroy
76
- @field = Field.find(params[:id])
76
+ @field = Field.find(params["id"])
77
77
  @field.destroy
78
78
 
79
79
  respond_with(@field)
@@ -82,7 +82,7 @@ class Admin::FieldsController < Admin::ApplicationController
82
82
  # POST /fields/sort
83
83
  #----------------------------------------------------------------------------
84
84
  def sort
85
- field_group_id = params[:field_group_id].to_i
85
+ field_group_id = params["field_group_id"].to_i
86
86
  field_ids = params["fields_field_group_#{field_group_id}"] || []
87
87
 
88
88
  field_ids.each_with_index do |id, index|
@@ -96,13 +96,12 @@ class Admin::FieldsController < Admin::ApplicationController
96
96
  #----------------------------------------------------------------------------
97
97
  def subform
98
98
  field = field_params
99
- as = field[:as]
100
-
99
+ as = field_params["as"]
101
100
  @field = if (id = field[:id]).present?
102
101
  Field.find(id).tap { |f| f.as = as }
103
102
  else
104
103
  field_group_id = field[:field_group_id]
105
- klass = find_class(Field.lookup_class(as))
104
+ klass = Field.lookup_class(as).safe_constantize
106
105
  klass.new(field_group_id: field_group_id, as: as)
107
106
  end
108
107
 
@@ -114,7 +113,11 @@ class Admin::FieldsController < Admin::ApplicationController
114
113
  protected
115
114
 
116
115
  def field_params
117
- params[:field].permit!
116
+ params.require(:field).permit(:as, :collection_string, :disabled, :field_group_id, :hint, :label, :maxlength, :minlength, :name, :pair_id, :placeholder, :position, :required, :type, settings: {})
117
+ end
118
+
119
+ def pair_params
120
+ params.require(:pair).permit("0": [:hint, :required, :disabled, :id], "1": [:hint, :required, :disabled, :id])
118
121
  end
119
122
 
120
123
  def setup_current_tab
@@ -28,7 +28,7 @@ class Admin::TagsController < Admin::ApplicationController
28
28
  # GET /admin/tags/1/edit AJAX
29
29
  #----------------------------------------------------------------------------
30
30
  def edit
31
- @previous = Tag.find_by_id(Regexp.last_match[1]) || Regexp.last_match[1].to_i if params[:previous].to_s =~ /(\d+)\z/
31
+ @previous = Tag.find_by_id(detect_previous_id) || detect_previous_id if detect_previous_id
32
32
  end
33
33
 
34
34
  # POST /admin/tags
@@ -35,7 +35,7 @@ class Admin::UsersController < Admin::ApplicationController
35
35
  # GET /admin/users/1/edit AJAX
36
36
  #----------------------------------------------------------------------------
37
37
  def edit
38
- @previous = User.find_by_id(Regexp.last_match[1]) || Regexp.last_match[1].to_i if params[:previous].to_s =~ /(\d+)\z/
38
+ @previous = User.find_by_id(detect_previous_id) || detect_previous_id if detect_previous_id
39
39
 
40
40
  respond_with(@user)
41
41
  end
@@ -268,4 +268,15 @@ class ApplicationController < ActionController::Base
268
268
  raise "Unknown resource"
269
269
  end
270
270
  end
271
+
272
+ # In a number of places, we pass ?previous=(id) or ?previous=crm.find_form...
273
+ # This method centralises all of the places we can pass in a previous param
274
+ # and extracts an int ID, or nil
275
+ def detect_previous_id
276
+ return unless params[:previous]
277
+ return if params[:previous].start_with?("crm")
278
+
279
+ params[:previous].to_i
280
+ end
281
+ ActiveSupport.run_load_hooks(:fat_free_crm_application_controller, self)
271
282
  end
@@ -99,4 +99,6 @@ class CommentsController < ApplicationController
99
99
  def extract_commentable_name(params)
100
100
  params.keys.detect { |x| x =~ /_id$/ }.try(:sub, /_id$/, '')
101
101
  end
102
+
103
+ ActiveSupport.run_load_hooks(:fat_free_crm_comments_controller, self)
102
104
  end
@@ -15,4 +15,6 @@ class EmailsController < ApplicationController
15
15
  @email.destroy
16
16
  respond_with(@email)
17
17
  end
18
+
19
+ ActiveSupport.run_load_hooks(:fat_free_crm_emails_controller, self)
18
20
  end
@@ -45,7 +45,7 @@ class AccountsController < EntitiesController
45
45
  # GET /accounts/1/edit AJAX
46
46
  #----------------------------------------------------------------------------
47
47
  def edit
48
- @previous = Account.my(current_user).find_by_id(Regexp.last_match[1]) || Regexp.last_match[1].to_i if params[:previous].to_s =~ /(\d+)\z/
48
+ @previous = Account.my(current_user).find_by_id(detect_previous_id) || detect_previous_id if detect_previous_id
49
49
 
50
50
  respond_with(@account)
51
51
  end
@@ -160,4 +160,6 @@ class AccountsController < EntitiesController
160
160
  @account_category_total[:all] = Account.my(current_user).count
161
161
  @account_category_total[:other] = @account_category_total[:all] - categorized
162
162
  end
163
+
164
+ ActiveSupport.run_load_hooks(:fat_free_crm_accounts_controller, self)
163
165
  end
@@ -84,7 +84,7 @@ class CampaignsController < EntitiesController
84
84
  # GET /campaigns/1/edit AJAX
85
85
  #----------------------------------------------------------------------------
86
86
  def edit
87
- @previous = Campaign.my(current_user).find_by_id(Regexp.last_match[1]) || Regexp.last_match[1].to_i if params[:previous].to_s =~ /(\d+)\z/
87
+ @previous = Campaign.my(current_user).find_by_id(detect_previous_id) || detect_previous_id if detect_previous_id
88
88
 
89
89
  respond_with(@campaign)
90
90
  end
@@ -204,4 +204,6 @@ class CampaignsController < EntitiesController
204
204
  end
205
205
  @campaign_status_total[:other] += @campaign_status_total[:all]
206
206
  end
207
+
208
+ ActiveSupport.run_load_hooks(:fat_free_crm_campaigns_controller, self)
207
209
  end
@@ -51,7 +51,7 @@ class ContactsController < EntitiesController
51
51
  #----------------------------------------------------------------------------
52
52
  def edit
53
53
  @account = @contact.account || Account.new(user: current_user)
54
- @previous = Contact.my(current_user).find_by_id(Regexp.last_match[1]) || Regexp.last_match[1].to_i if params[:previous].to_s =~ /(\d+)\z/
54
+ @previous = Contact.my(current_user).find_by_id(detect_previous_id) || detect_previous_id if detect_previous_id
55
55
 
56
56
  respond_with(@contact)
57
57
  end
@@ -164,4 +164,6 @@ class ContactsController < EntitiesController
164
164
  redirect_to contacts_path
165
165
  end
166
166
  end
167
+
168
+ ActiveSupport.run_load_hooks(:fat_free_crm_contacts_controller, self)
167
169
  end
@@ -52,7 +52,7 @@ class LeadsController < EntitiesController
52
52
  def edit
53
53
  get_campaigns
54
54
 
55
- @previous = Lead.my(current_user).find_by_id(Regexp.last_match[1]) || Regexp.last_match[1].to_i if params[:previous].to_s =~ /(\d+)\z/
55
+ @previous = Lead.my(current_user).find_by_id(detect_previous_id) || detect_previous_id if detect_previous_id
56
56
 
57
57
  respond_with(@lead)
58
58
  end
@@ -108,7 +108,7 @@ class LeadsController < EntitiesController
108
108
  @accounts = Account.my(current_user).order('name')
109
109
  @opportunity = Opportunity.new(user: current_user, access: "Lead", stage: "prospecting", campaign: @lead.campaign, source: @lead.source)
110
110
 
111
- @previous = Lead.my(current_user).find_by_id(Regexp.last_match[1]) || Regexp.last_match[1].to_i if params[:previous].to_s =~ /(\d+)\z/
111
+ @previous = Lead.my(current_user).find_by_id(detect_previous_id) || detect_previous_id if detect_previous_id
112
112
 
113
113
  respond_with(@lead)
114
114
  end
@@ -268,4 +268,6 @@ class LeadsController < EntitiesController
268
268
  get_data_for_sidebar(:campaign)
269
269
  end
270
270
  end
271
+
272
+ ActiveSupport.run_load_hooks(:fat_free_crm_leads_controller, self)
271
273
  end
@@ -57,7 +57,7 @@ class OpportunitiesController < EntitiesController
57
57
  @account = @opportunity.account || Account.new(user: current_user)
58
58
  @accounts = Account.my(current_user).order('name')
59
59
 
60
- @previous = Opportunity.my(current_user).find_by_id(Regexp.last_match[1]) || Regexp.last_match[1].to_i if params[:previous].to_s =~ /(\d+)\z/
60
+ @previous = Opportunity.my(current_user).find_by_id(detect_previous_id) || detect_previous_id if detect_previous_id
61
61
 
62
62
  respond_with(@opportunity)
63
63
  end
@@ -227,4 +227,6 @@ class OpportunitiesController < EntitiesController
227
227
  current_user.pref[:opportunities_sort_by] = Opportunity.sort_by_map[params[:sort_by]] if params[:sort_by]
228
228
  session[:opportunities_filter] = params[:stage] if params[:stage]
229
229
  end
230
+
231
+ ActiveSupport.run_load_hooks(:fat_free_crm_opportunities_controller, self)
230
232
  end
@@ -83,7 +83,7 @@ class EntitiesController < ApplicationController
83
83
  #----------------------------------------------------------------------------
84
84
  def field_group
85
85
  if @tag = Tag.find_by_name(params[:tag].strip)
86
- if @field_group = FieldGroup.find_by_tag_id_and_klass_name(@tag.id, klass.to_s)
86
+ if @field_groups = FieldGroup.where(tag_id: @tag.id, klass_name: klass.to_s).order(:label, :created_at)
87
87
  @asset = klass.find_by_id(params[:asset_id]) || klass.new
88
88
  render('fields/group') && return
89
89
  end
@@ -245,4 +245,6 @@ class EntitiesController < ApplicationController
245
245
  Account.new(user: user)
246
246
  end
247
247
  end
248
+
249
+ ActiveSupport.run_load_hooks(:fat_free_crm_entities_controller, self)
248
250
  end
@@ -165,4 +165,6 @@ class HomeController < ApplicationController
165
165
  %w[zero one two].index(words.first).send(words.last) if %w[one two].include?(words.first) && %w[hour day days week weeks month].include?(words.last)
166
166
  end
167
167
  end
168
+
169
+ ActiveSupport.run_load_hooks(:fat_free_crm_home_controller, self)
168
170
  end
@@ -9,13 +9,14 @@ class ListsController < ApplicationController
9
9
  # POST /lists
10
10
  #----------------------------------------------------------------------------
11
11
  def create
12
- list_params[:user_id] = (current_user.id if params[:is_global].to_i.zero?)
12
+ list_attr = list_params.to_h
13
+ list_attr["user_id"] = current_user.id if params["is_global"] != "1"
13
14
 
14
15
  # Find any existing list with the same name (case insensitive)
15
- if @list = List.where("lower(name) = ?", list_params[:name].downcase).where(user_id: list_params[:user_id]).first
16
- @list.update(list_params)
16
+ if @list = List.where("lower(name) = ?", list_attr[:name].downcase).where(user_id: list_attr[:user_id]).first
17
+ @list.update(list_attr)
17
18
  else
18
- @list = List.create(list_params)
19
+ @list = List.create(list_attr)
19
20
  end
20
21
 
21
22
  respond_with(@list)
@@ -39,4 +40,6 @@ class ListsController < ApplicationController
39
40
  :user_id
40
41
  )
41
42
  end
43
+
44
+ ActiveSupport.run_load_hooks(:fat_free_crm_lists_controller, self)
42
45
  end
@@ -58,7 +58,7 @@ class TasksController < ApplicationController
58
58
  @category = Setting.unroll(:task_category)
59
59
  @asset = @task.asset if @task.asset_id?
60
60
 
61
- @previous = Task.tracked_by(current_user).find_by_id(Regexp.last_match[1]) || Regexp.last_match[1].to_i if params[:previous].to_s =~ /(\d+)\z/
61
+ @previous = Task.tracked_by(current_user).find_by_id(detect_previous_id) || detect_previous_id if detect_previous_id
62
62
 
63
63
  respond_with(@task)
64
64
  end
@@ -224,4 +224,6 @@ class TasksController < ApplicationController
224
224
  views = Task::ALLOWED_VIEWS
225
225
  views.include?(view) ? view : views.first
226
226
  end
227
+
228
+ ActiveSupport.run_load_hooks(:fat_free_crm_tasks_controller, self)
227
229
  end
@@ -143,4 +143,6 @@ class UsersController < ApplicationController
143
143
  .permit(:image)
144
144
  .merge(entity: @user, user_id: @user.id)
145
145
  end
146
+
147
+ ActiveSupport.run_load_hooks(:fat_free_crm_users_controller, self)
146
148
  end
@@ -36,7 +36,7 @@ module ApplicationHelper
36
36
  end
37
37
 
38
38
  def subtitle_link(id, text, hidden)
39
- link_to("<small>#{hidden ? '&#9658;' : '&#9660;'}</small> #{sanitize text}".html_safe,
39
+ link_to("<small>#{hidden ? '&#9655;' : '&#9661;'}</small> #{sanitize text}".html_safe,
40
40
  url_for(controller: :home, action: :toggle, id: id),
41
41
  remote: true,
42
42
  onclick: "crm.flip_subtitle(this)")
@@ -99,7 +99,7 @@ module ApplicationHelper
99
99
 
100
100
  #----------------------------------------------------------------------------
101
101
  def arrow_for(id)
102
- content_tag(:span, "&#9658;".html_safe, id: "#{id}_arrow", class: :arrow)
102
+ content_tag(:span, "&#9655;".html_safe, id: "#{id}_arrow", class: :arrow)
103
103
  end
104
104
 
105
105
  #----------------------------------------------------------------------------
@@ -115,13 +115,13 @@ module ApplicationHelper
115
115
 
116
116
  #----------------------------------------------------------------------------
117
117
  def link_to_delete(record, options = {})
118
- confirm = options[:confirm] || nil
118
+ confirm = options[:confirm] || t(:confirm_delete, record.class.to_s.downcase)
119
119
 
120
120
  link_to(t(:delete) + "!",
121
121
  options[:url] || url_for(record),
122
122
  method: :delete,
123
123
  remote: true,
124
- confirm: confirm)
124
+ data: { confirm: confirm })
125
125
  end
126
126
 
127
127
  #----------------------------------------------------------------------------
@@ -41,8 +41,10 @@ module OpportunitiesHelper
41
41
  selected_campaign = Campaign.find_by_id(options[:selected])
42
42
  campaigns = ([selected_campaign] + Campaign.my(current_user).order(:name).limit(25)).compact.uniq
43
43
  collection_select :opportunity, :campaign_id, campaigns, :id, :name,
44
- { selected: options[:selected], prompt: t(:select_a_campaign) },
45
- style: 'width:330px;', class: 'select2'
44
+ { selected: options[:selected], prompt: t(:select_a_campaign), include_blank: true },
45
+ style: 'width:330px;', class: 'select2',
46
+ placeholder: t(:select_a_campaign),
47
+ "data-url": auto_complete_campaigns_path(format: 'json')
46
48
  end
47
49
 
48
50
  # Generates the inline revenue message for the opportunity list table.
@@ -25,7 +25,7 @@ module UsersHelper
25
25
  user_options = user_options_for_select(users, myself)
26
26
  select(asset, :assigned_to, user_options,
27
27
  { include_blank: t(:unassigned) },
28
- style: 'width: 160px;',
28
+ style: 'width: 160px;', "data-allow-clear" => false,
29
29
  class: 'select2')
30
30
  end
31
31
 
@@ -35,8 +35,8 @@ class Campaign < ActiveRecord::Base
35
35
  belongs_to :user, optional: true # TODO: Is this really optional?
36
36
  belongs_to :assignee, class_name: "User", foreign_key: :assigned_to, optional: true # TODO: Is this really optional?
37
37
  has_many :tasks, as: :asset, dependent: :destroy # , :order => 'created_at DESC'
38
- has_many :leads, -> { order "id DESC" }, dependent: :destroy
39
- has_many :opportunities, -> { order "id DESC" }, dependent: :destroy
38
+ has_many :leads, -> { order "id DESC" }
39
+ has_many :opportunities, -> { order "id DESC" }
40
40
  has_many :emails, as: :mediator
41
41
 
42
42
  serialize :subscribed_users, Array
@@ -124,7 +124,7 @@ class CustomField < Field
124
124
  #------------------------------------------------------------------------------
125
125
  def add_column
126
126
  self.name = generate_column_name if name.blank?
127
- klass.connection.add_column(table_name, name, column_type, column_options)
127
+ klass.connection.add_column(table_name, name, column_type, **column_options)
128
128
  klass.reset_column_information
129
129
  klass.serialize_custom_fields!
130
130
  end
@@ -142,7 +142,7 @@ class CustomField < Field
142
142
  #------------------------------------------------------------------------------
143
143
  def update_column
144
144
  if errors.empty? && db_transition_safety(as_was) == :safe
145
- klass.connection.change_column(table_name, name, column_type, column_options)
145
+ klass.connection.change_column(table_name, name, column_type, **column_options)
146
146
  klass.reset_column_information
147
147
  klass.serialize_custom_fields!
148
148
  end
@@ -12,10 +12,9 @@ class CustomFieldPair < CustomField
12
12
  #------------------------------------------------------------------------------
13
13
  def self.create_pair(params)
14
14
  fields = params['field']
15
- as = params['field']['as']
16
- pair = params.delete('pair')
15
+ pair = params['pair']
17
16
  base_params = fields.delete_if { |k, _v| !%w[field_group_id label as].include?(k) }
18
- klass = ("custom_field_" + as.gsub('pair', '_pair')).classify.constantize
17
+ klass = Field.lookup_class(fields['as']).safe_constantize
19
18
  field1 = klass.create(base_params.merge(pair['0']))
20
19
  field2 = klass.create(base_params.merge(pair['1']).merge('pair_id' => field1.id, 'required' => field1.required, 'disabled' => field1.disabled))
21
20
  [field1, field2]
@@ -25,19 +24,19 @@ class CustomFieldPair < CustomField
25
24
  #------------------------------------------------------------------------------
26
25
  def self.update_pair(params)
27
26
  fields = params['field']
28
- pair = params.delete('pair')
27
+ pair = params['pair']
29
28
  base_params = fields.delete_if { |k, _v| !%w[field_group_id label as].include?(k) }
30
- field1 = CustomFieldPair.find(params['id'])
29
+ field1 = CustomFieldPair.find(pair['0']['id'])
31
30
  field1.update(base_params.merge(pair['0']))
32
31
  field2 = field1.paired_with
33
32
  field2.update(base_params.merge(pair['1']).merge('required' => field1.required, 'disabled' => field1.disabled))
34
33
  [field1, field2]
35
34
  end
36
35
 
37
- # Returns the field that this field is paired with
36
+ # Returns the field that this field is paired with (bi-directional)
38
37
  #------------------------------------------------------------------------------
39
38
  def paired_with
40
- pair || CustomFieldPair.where(pair_id: id).first
39
+ pair || self.class.find_by_id(pair_id)
41
40
  end
42
41
 
43
42
  ActiveSupport.run_load_hooks(:fat_free_crm_custom_field_pair, self)
@@ -34,7 +34,7 @@ class Field < ActiveRecord::Base
34
34
  serialize :collection, Array
35
35
  serialize :settings, HashWithIndifferentAccess
36
36
 
37
- belongs_to :field_group, optional: true # TODO: Is this really optional?
37
+ belongs_to :field_group, optional: true
38
38
 
39
39
  scope :core_fields, -> { where(type: 'CoreField') }
40
40
  scope :custom_fields, -> { where("type != 'CoreField'") }
@@ -92,8 +92,6 @@ class Field < ActiveRecord::Base
92
92
 
93
93
  def render(value)
94
94
  case as
95
- when 'checkbox'
96
- value.to_s == '0' ? "no" : "yes"
97
95
  when 'date'
98
96
  value&.strftime(I18n.t("date.formats.mmddyy"))
99
97
  when 'datetime'
data/app/models/list.rb CHANGED
@@ -8,7 +8,7 @@
8
8
  class List < ActiveRecord::Base
9
9
  validates_presence_of :name
10
10
  validates_presence_of :url
11
- belongs_to :user
11
+ belongs_to :user, optional: true
12
12
 
13
13
  # Parses the controller from the url
14
14
  def controller