fat_free_crm 0.19.2 → 0.20.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


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

Files changed (226) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/brakeman-analysis.yml +46 -0
  3. data/.github/workflows/rubocop-analysis.yml +40 -0
  4. data/.github/workflows/ruby.yml +52 -0
  5. data/.rubocop_todo.yml +49 -43
  6. data/.travis.yml +1 -20
  7. data/CHANGELOG.md +13 -0
  8. data/Gemfile +9 -5
  9. data/Gemfile.lock +163 -119
  10. data/README.md +4 -3
  11. data/SECURITY.md +11 -0
  12. data/app/assets/javascripts/application.js.erb +3 -0
  13. data/app/assets/stylesheets/application.css.erb +1 -0
  14. data/app/assets/stylesheets/base.scss +9 -2
  15. data/app/assets/stylesheets/bootstrap-custom.scss +206 -0
  16. data/app/assets/stylesheets/common.scss +87 -67
  17. data/app/assets/stylesheets/header.scss +0 -8
  18. data/app/controllers/admin/field_groups_controller.rb +1 -1
  19. data/app/controllers/admin/fields_controller.rb +1 -1
  20. data/app/controllers/admin/groups_controller.rb +1 -1
  21. data/app/controllers/admin/tags_controller.rb +2 -2
  22. data/app/controllers/admin/users_controller.rb +2 -1
  23. data/app/controllers/application_controller.rb +10 -0
  24. data/app/controllers/comments_controller.rb +1 -1
  25. data/app/controllers/entities/accounts_controller.rb +1 -1
  26. data/app/controllers/entities/campaigns_controller.rb +1 -1
  27. data/app/controllers/home_controller.rb +3 -1
  28. data/app/controllers/lists_controller.rb +1 -1
  29. data/app/controllers/tasks_controller.rb +3 -3
  30. data/app/controllers/users_controller.rb +3 -1
  31. data/app/helpers/accounts_helper.rb +12 -0
  32. data/app/helpers/application_helper.rb +3 -5
  33. data/app/helpers/tasks_helper.rb +2 -2
  34. data/app/models/entities/account.rb +1 -1
  35. data/app/models/entities/account_contact.rb +1 -1
  36. data/app/models/entities/account_opportunity.rb +1 -1
  37. data/app/models/entities/campaign.rb +1 -1
  38. data/app/models/entities/contact.rb +1 -1
  39. data/app/models/entities/lead.rb +1 -1
  40. data/app/models/entities/opportunity.rb +1 -1
  41. data/app/models/fields/custom_field_pair.rb +2 -2
  42. data/app/models/observers/opportunity_observer.rb +1 -1
  43. data/app/models/polymorphic/address.rb +1 -1
  44. data/app/models/polymorphic/comment.rb +1 -1
  45. data/app/models/polymorphic/email.rb +1 -1
  46. data/app/models/polymorphic/task.rb +1 -1
  47. data/app/models/setting.rb +4 -2
  48. data/app/models/users/preference.rb +2 -2
  49. data/app/models/users/user.rb +2 -2
  50. data/app/views/accounts/_edit.html.haml +2 -2
  51. data/app/views/accounts/_new.html.haml +2 -2
  52. data/app/views/accounts/_sidebar_index.html.haml +2 -2
  53. data/app/views/accounts/_sidebar_show.html.haml +19 -19
  54. data/app/views/accounts/create.js.haml +1 -2
  55. data/app/views/accounts/destroy.js.haml +1 -1
  56. data/app/views/accounts/show.html.haml +1 -0
  57. data/app/views/accounts/update.js.haml +2 -3
  58. data/app/views/admin/field_groups/create.js.haml +0 -1
  59. data/app/views/admin/field_groups/destroy.js.haml +0 -1
  60. data/app/views/admin/field_groups/update.js.haml +0 -1
  61. data/app/views/admin/fields/create.js.haml +0 -1
  62. data/app/views/admin/fields/destroy.js.haml +0 -1
  63. data/app/views/admin/fields/update.js.haml +0 -1
  64. data/app/views/admin/groups/create.js.haml +0 -1
  65. data/app/views/admin/groups/destroy.js.haml +0 -1
  66. data/app/views/admin/groups/update.js.haml +0 -1
  67. data/app/views/admin/tags/create.js.haml +0 -1
  68. data/app/views/admin/tags/destroy.js.haml +0 -1
  69. data/app/views/admin/tags/update.js.haml +0 -1
  70. data/app/views/admin/users/create.js.haml +0 -1
  71. data/app/views/admin/users/destroy.js.haml +0 -1
  72. data/app/views/admin/users/update.js.haml +0 -1
  73. data/app/views/campaigns/_edit.html.haml +1 -1
  74. data/app/views/campaigns/_new.html.haml +1 -1
  75. data/app/views/campaigns/_sidebar_index.html.haml +2 -2
  76. data/app/views/campaigns/_sidebar_show.html.haml +68 -67
  77. data/app/views/campaigns/create.js.haml +1 -2
  78. data/app/views/campaigns/destroy.js.haml +1 -1
  79. data/app/views/campaigns/show.html.haml +1 -0
  80. data/app/views/campaigns/update.js.haml +3 -4
  81. data/app/views/comments/_edit.html.haml +1 -1
  82. data/app/views/comments/_new.html.haml +1 -1
  83. data/app/views/comments/update.js.haml +0 -1
  84. data/app/views/contacts/_edit.html.haml +1 -1
  85. data/app/views/contacts/_new.html.haml +1 -1
  86. data/app/views/contacts/_sidebar_show.html.haml +18 -18
  87. data/app/views/contacts/create.js.haml +0 -1
  88. data/app/views/contacts/destroy.js.haml +1 -1
  89. data/app/views/contacts/show.html.haml +1 -0
  90. data/app/views/contacts/update.js.haml +2 -3
  91. data/app/views/entities/_title_bar.html.haml +1 -1
  92. data/app/views/entities/attach.js.haml +2 -2
  93. data/app/views/entities/discard.js.haml +2 -2
  94. data/app/views/home/_opportunity.html.haml +1 -1
  95. data/app/views/home/_task.html.haml +1 -1
  96. data/app/views/home/index.xls.builder +51 -0
  97. data/app/views/layouts/_sidebar.html.haml +2 -2
  98. data/app/views/layouts/_tabbed.html.haml +14 -11
  99. data/app/views/layouts/application.html.haml +2 -2
  100. data/app/views/leads/_edit.html.haml +2 -2
  101. data/app/views/leads/_new.html.haml +2 -2
  102. data/app/views/leads/_sidebar_index.html.haml +2 -2
  103. data/app/views/leads/_sidebar_show.html.haml +30 -30
  104. data/app/views/leads/create.js.haml +2 -3
  105. data/app/views/leads/destroy.js.haml +2 -2
  106. data/app/views/leads/promote.js.haml +3 -4
  107. data/app/views/leads/reject.js.haml +3 -3
  108. data/app/views/leads/show.html.haml +1 -1
  109. data/app/views/leads/update.js.haml +4 -5
  110. data/app/views/lists/create.js.haml +0 -1
  111. data/app/views/opportunities/_edit.html.haml +1 -1
  112. data/app/views/opportunities/_index_brief.html.haml +1 -1
  113. data/app/views/opportunities/_index_long.html.haml +1 -1
  114. data/app/views/opportunities/_new.html.haml +1 -1
  115. data/app/views/opportunities/_sidebar_index.html.haml +2 -2
  116. data/app/views/opportunities/_sidebar_show.html.haml +47 -46
  117. data/app/views/opportunities/create.js.haml +3 -4
  118. data/app/views/opportunities/destroy.js.haml +3 -3
  119. data/app/views/opportunities/show.html.haml +1 -0
  120. data/app/views/opportunities/update.js.haml +5 -6
  121. data/app/views/shared/_inline_styles.html.haml +0 -26
  122. data/app/views/tasks/_assigned.html.haml +1 -1
  123. data/app/views/tasks/_completed.html.haml +1 -1
  124. data/app/views/tasks/_edit.html.haml +1 -1
  125. data/app/views/tasks/_new.html.haml +1 -1
  126. data/app/views/tasks/_pending.html.haml +1 -1
  127. data/app/views/tasks/_related.html.haml +1 -1
  128. data/app/views/tasks/_selector.html.haml +7 -8
  129. data/app/views/tasks/_sidebar_index.html.haml +2 -2
  130. data/app/views/tasks/_title.html.haml +1 -1
  131. data/app/views/tasks/complete.js.haml +1 -1
  132. data/app/views/tasks/create.js.haml +1 -2
  133. data/app/views/tasks/destroy.js.haml +1 -1
  134. data/app/views/tasks/uncomplete.js.haml +1 -2
  135. data/app/views/tasks/update.js.haml +0 -1
  136. data/app/views/users/change_password.js.haml +0 -1
  137. data/app/views/users/update.js.haml +0 -1
  138. data/app/views/users/upload_avatar.js.haml +0 -1
  139. data/app/views/versions/_version_item.html.haml +1 -1
  140. data/bin/setup +6 -6
  141. data/config/application.rb +1 -1
  142. data/config/environment.rb +1 -1
  143. data/config/initializers/content_security_policy.rb +5 -0
  144. data/config/initializers/new_framework_defaults_6_0.rb +46 -0
  145. data/config/initializers/simple_form.rb +135 -55
  146. data/config/initializers/simple_form_bootstrap.rb +433 -0
  147. data/config/locales/fat_free_crm.cs.yml +86 -47
  148. data/config/locales/fat_free_crm.ru.yml +1 -0
  149. data/config/settings.default.yml +0 -41
  150. data/db/migrate/20200806004152_add_pattern_to_fields.rb +7 -0
  151. data/db/migrate/20200806004459_add_html5_to_fields.rb +10 -0
  152. data/db/schema.rb +11 -6
  153. data/fat_free_crm.gemspec +4 -4
  154. data/lib/fat_free_crm/core_ext.rb +1 -1
  155. data/lib/fat_free_crm/gem_ext.rb +0 -1
  156. data/lib/fat_free_crm/tabs.rb +2 -2
  157. data/lib/fat_free_crm/version.rb +2 -2
  158. data/lib/gravatar_image_tag.rb +1 -1
  159. data/lib/tasks/ffcrm/preference_update.rake +19 -0
  160. data/lib/tasks/ffcrm/setup.rake +1 -1
  161. data/lib/templates/erb/scaffold/_form.html.erb +4 -2
  162. data/spec/controllers/comments_controller_spec.rb +6 -6
  163. data/spec/controllers/home_controller_spec.rb +3 -3
  164. data/spec/controllers/users_controller_spec.rb +13 -1
  165. data/spec/factories/user_factories.rb +5 -2
  166. data/spec/features/acceptance_helper.rb +1 -1
  167. data/spec/features/accounts_spec.rb +2 -2
  168. data/spec/features/admin/groups_spec.rb +1 -1
  169. data/spec/features/admin/users_spec.rb +1 -1
  170. data/spec/features/campaigns_spec.rb +2 -2
  171. data/spec/features/contacts_spec.rb +2 -5
  172. data/spec/features/leads_spec.rb +11 -7
  173. data/spec/features/opportunities_spec.rb +4 -4
  174. data/spec/features/support/browser.rb +6 -2
  175. data/spec/features/support/selector_helpers.rb +10 -1
  176. data/spec/features/tasks_spec.rb +6 -6
  177. data/spec/helpers/accounts_helper_spec.rb +57 -0
  178. data/spec/models/entities/opportunity_spec.rb +2 -2
  179. data/spec/models/fields/custom_field_pair_spec.rb +2 -2
  180. data/spec/models/observers/entity_observer_spec.rb +3 -3
  181. data/spec/models/polymorphic/task_spec.rb +11 -11
  182. data/spec/models/polymorphic/version_spec.rb +2 -2
  183. data/spec/models/users/preference_spec.rb +1 -1
  184. data/spec/views/accounts/_edit.haml_spec.rb +1 -1
  185. data/spec/views/accounts/_new.haml_spec.rb +1 -1
  186. data/spec/views/accounts/create.js.haml_spec.rb +0 -1
  187. data/spec/views/accounts/update.js.haml_spec.rb +2 -5
  188. data/spec/views/admin/field_groups/create.js.haml_spec.rb +0 -1
  189. data/spec/views/admin/field_groups/update.js.haml_spec.rb +0 -1
  190. data/spec/views/admin/users/create.js.haml_spec.rb +0 -1
  191. data/spec/views/admin/users/destroy.js.haml_spec.rb +0 -6
  192. data/spec/views/admin/users/update.js.haml_spec.rb +1 -2
  193. data/spec/views/campaigns/_edit.haml_spec.rb +1 -1
  194. data/spec/views/campaigns/_new.haml_spec.rb +1 -1
  195. data/spec/views/campaigns/create.js.haml_spec.rb +0 -1
  196. data/spec/views/campaigns/destroy.js.haml_spec.rb +0 -1
  197. data/spec/views/campaigns/update.js.haml_spec.rb +2 -6
  198. data/spec/views/contacts/_edit.haml_spec.rb +1 -1
  199. data/spec/views/contacts/_new.haml_spec.rb +1 -1
  200. data/spec/views/contacts/create.js.haml_spec.rb +0 -1
  201. data/spec/views/contacts/update.js.haml_spec.rb +3 -7
  202. data/spec/views/home/index.haml_spec.rb +2 -2
  203. data/spec/views/leads/_edit.haml_spec.rb +1 -1
  204. data/spec/views/leads/_new.haml_spec.rb +1 -1
  205. data/spec/views/leads/create.js.haml_spec.rb +0 -3
  206. data/spec/views/leads/destroy.js.haml_spec.rb +0 -2
  207. data/spec/views/leads/promote.js.haml_spec.rb +3 -11
  208. data/spec/views/leads/reject.js.haml_spec.rb +0 -3
  209. data/spec/views/leads/update.js.haml_spec.rb +3 -11
  210. data/spec/views/opportunities/_edit.haml_spec.rb +1 -1
  211. data/spec/views/opportunities/_new.haml_spec.rb +1 -1
  212. data/spec/views/opportunities/create.js.haml_spec.rb +0 -2
  213. data/spec/views/opportunities/destroy.js.haml_spec.rb +0 -3
  214. data/spec/views/opportunities/update.js.haml_spec.rb +3 -11
  215. data/spec/views/tasks/_edit.haml_spec.rb +1 -1
  216. data/spec/views/tasks/complete.js.haml_spec.rb +0 -1
  217. data/spec/views/tasks/create.js.haml_spec.rb +0 -2
  218. data/spec/views/tasks/destroy.js.haml_spec.rb +0 -1
  219. data/spec/views/tasks/uncomplete.js.haml_spec.rb +0 -1
  220. data/spec/views/tasks/update.js.haml_spec.rb +1 -4
  221. data/spec/views/users/change_password.js.haml_spec.rb +1 -2
  222. data/spec/views/users/update.js.haml_spec.rb +1 -2
  223. data/spec/views/users/upload_avatar.js.haml_spec.rb +1 -2
  224. metadata +23 -14
  225. data/config/initializers/new_framework_defaults_5_2.rb +0 -40
  226. data/lib/fat_free_crm/gem_ext/simple_form/action_view_extensions/form_helper.rb +0 -24
@@ -13,7 +13,7 @@ class ListsController < ApplicationController
13
13
 
14
14
  # Find any existing list with the same name (case insensitive)
15
15
  if @list = List.where("lower(name) = ?", list_params[:name].downcase).where(user_id: list_params[:user_id]).first
16
- @list.update_attributes(list_params)
16
+ @list.update(list_params)
17
17
  else
18
18
  @list = List.create(list_params)
19
19
  end
@@ -90,7 +90,7 @@ class TasksController < ApplicationController
90
90
  end
91
91
 
92
92
  respond_with(@task) do |_format|
93
- if @task.update_attributes(task_params)
93
+ if @task.update(task_params)
94
94
  @task.bucket = @task.computed_bucket
95
95
  if called_from_index_page?
96
96
  @empty_bucket = @task_before_update.bucket if Task.bucket_empty?(@task_before_update.bucket, current_user, @view)
@@ -118,7 +118,7 @@ class TasksController < ApplicationController
118
118
  #----------------------------------------------------------------------------
119
119
  def complete
120
120
  @task = Task.tracked_by(current_user).find(params[:id])
121
- @task&.update_attributes(completed_at: Time.now, completed_by: current_user.id)
121
+ @task&.update(completed_at: Time.now, completed_by: current_user.id)
122
122
 
123
123
  # Make sure bucket's div gets hidden if it's the last completed task in the bucket.
124
124
  @empty_bucket = params[:bucket] if Task.bucket_empty?(params[:bucket], current_user)
@@ -131,7 +131,7 @@ class TasksController < ApplicationController
131
131
  #----------------------------------------------------------------------------
132
132
  def uncomplete
133
133
  @task = Task.tracked_by(current_user).find(params[:id])
134
- @task&.update_attributes(completed_at: nil, completed_by: nil)
134
+ @task&.update(completed_at: nil, completed_by: nil)
135
135
 
136
136
  # Make sure bucket's div gets hidden if we're deleting last task in the bucket.
137
137
  @empty_bucket = params[:bucket] if Task.bucket_empty?(params[:bucket], current_user, @view)
@@ -32,7 +32,7 @@ class UsersController < ApplicationController
32
32
  # PUT /users/1.js
33
33
  #----------------------------------------------------------------------------
34
34
  def update
35
- @user.update_attributes(user_params)
35
+ @user.update(user_params)
36
36
  flash[:notice] = t(:msg_user_updated)
37
37
  respond_with(@user)
38
38
  end
@@ -117,6 +117,8 @@ class UsersController < ApplicationController
117
117
  return {} unless params[:user]
118
118
 
119
119
  params[:user][:email].try(:strip!)
120
+ params[:user][:alt_email].try(:strip!)
121
+
120
122
  params[:user].permit(
121
123
  :username,
122
124
  :email,
@@ -104,4 +104,16 @@ module AccountsHelper
104
104
  end
105
105
  text.html_safe
106
106
  end
107
+
108
+ # We have too much logic in the account views
109
+ # - a helper that abstracts the logic to the backend
110
+ def display_value(value)
111
+ return "N/A" if value.zero?
112
+ number_to_currency(value, precision: 0)
113
+ end
114
+
115
+ def display_assigned(account)
116
+ return truncate(account.assignee.full_name, length: 16) if account.assigned_to
117
+ nil
118
+ end
107
119
  end
@@ -88,7 +88,6 @@ module ApplicationHelper
88
88
  #----------------------------------------------------------------------------
89
89
  def link_to_inline(id, url, options = {})
90
90
  text = options[:text] || t(id, default: id.to_s.titleize)
91
- text = (arrow_for(id) + text) unless options[:plain]
92
91
  related = (options[:related] ? "&related=#{options[:related]}" : '')
93
92
 
94
93
  link_to(text,
@@ -234,16 +233,15 @@ module ApplicationHelper
234
233
 
235
234
  # Reresh sidebar using the action view within the current controller.
236
235
  #----------------------------------------------------------------------------
237
- def refresh_sidebar(action = nil, shake = nil)
238
- refresh_sidebar_for(controller.controller_name, action, shake)
236
+ def refresh_sidebar(action = nil)
237
+ refresh_sidebar_for(controller.controller_name, action)
239
238
  end
240
239
 
241
240
  # Refresh sidebar using the action view within an arbitrary controller.
242
241
  #----------------------------------------------------------------------------
243
- def refresh_sidebar_for(view, action = nil, shake = nil)
242
+ def refresh_sidebar_for(view, action = nil)
244
243
  text = ""
245
244
  text += "$('#sidebar').html('#{j render(partial: 'layouts/sidebar', locals: { view: view, action: action })}');"
246
- text += "$('##{j shake.to_s}').effect('shake', { duration:200, distance: 3 });" if shake
247
245
  text.html_safe
248
246
  end
249
247
 
@@ -128,7 +128,7 @@ module TasksHelper
128
128
  else
129
129
  text << replace_content(@task, @task.bucket)
130
130
  end
131
- text << refresh_sidebar(:index, :filters)
131
+ text << refresh_sidebar(:index)
132
132
  text
133
133
  end
134
134
 
@@ -136,7 +136,7 @@ module TasksHelper
136
136
  def reschedule(task)
137
137
  text = hide_task_and_possibly_bucket(task, @task_before_update.bucket)
138
138
  text += insert_content(task, task.bucket, @view)
139
- text += refresh_sidebar(:index, :filters)
139
+ text += refresh_sidebar(:index)
140
140
  text
141
141
  end
142
142
  end
@@ -65,7 +65,7 @@ class Account < ActiveRecord::Base
65
65
  acts_as_commentable
66
66
  uses_comment_extensions
67
67
  acts_as_taggable_on :tags
68
- has_paper_trail class_name: 'Version', ignore: [:subscribed_users]
68
+ has_paper_trail versions: { class_name: 'Version' }, ignore: [:subscribed_users]
69
69
  has_fields
70
70
  exportable
71
71
  sortable by: ["name ASC", "rating DESC", "created_at DESC", "updated_at DESC"], default: "created_at DESC"
@@ -21,7 +21,7 @@ class AccountContact < ActiveRecord::Base
21
21
  belongs_to :account, counter_cache: :contacts_count
22
22
  belongs_to :contact
23
23
 
24
- has_paper_trail class_name: 'Version', meta: { related: :contact },
24
+ has_paper_trail versions: { class_name: 'Version' }, meta: { related: :contact },
25
25
  ignore: %i[id created_at updated_at contact_id]
26
26
 
27
27
  validates_presence_of :account_id
@@ -22,7 +22,7 @@ class AccountOpportunity < ActiveRecord::Base
22
22
  belongs_to :opportunity
23
23
  validates_presence_of :account_id, :opportunity_id
24
24
 
25
- has_paper_trail class_name: 'Version'
25
+ has_paper_trail versions: { class_name: 'Version' }
26
26
 
27
27
  ActiveSupport.run_load_hooks(:fat_free_crm_account_opportunity, self)
28
28
  end
@@ -53,7 +53,7 @@ class Campaign < ActiveRecord::Base
53
53
  acts_as_commentable
54
54
  uses_comment_extensions
55
55
  acts_as_taggable_on :tags
56
- has_paper_trail class_name: 'Version', ignore: [:subscribed_users]
56
+ has_paper_trail versions: { class_name: 'Version' }, ignore: [:subscribed_users]
57
57
  has_fields
58
58
  exportable
59
59
  sortable by: ["name ASC", "target_leads DESC", "target_revenue DESC", "leads_count DESC", "revenue DESC", "starts_on DESC", "ends_on DESC", "created_at DESC", "updated_at DESC"], default: "created_at DESC"
@@ -88,7 +88,7 @@ class Contact < ActiveRecord::Base
88
88
  acts_as_commentable
89
89
  uses_comment_extensions
90
90
  acts_as_taggable_on :tags
91
- has_paper_trail class_name: 'Version', ignore: [:subscribed_users]
91
+ has_paper_trail versions: { class_name: 'Version' }, ignore: [:subscribed_users]
92
92
 
93
93
  has_fields
94
94
  exportable
@@ -66,7 +66,7 @@ class Lead < ActiveRecord::Base
66
66
  acts_as_commentable
67
67
  uses_comment_extensions
68
68
  acts_as_taggable_on :tags
69
- has_paper_trail class_name: 'Version', ignore: [:subscribed_users]
69
+ has_paper_trail versions: { class_name: 'Version' }, ignore: [:subscribed_users]
70
70
  has_fields
71
71
  exportable
72
72
  sortable by: ["first_name ASC", "last_name ASC", "company ASC", "rating DESC", "created_at DESC", "updated_at DESC"], default: "created_at DESC"
@@ -73,7 +73,7 @@ class Opportunity < ActiveRecord::Base
73
73
  acts_as_commentable
74
74
  uses_comment_extensions
75
75
  acts_as_taggable_on :tags
76
- has_paper_trail class_name: 'Version', ignore: [:subscribed_users]
76
+ has_paper_trail versions: { class_name: 'Version' }, ignore: [:subscribed_users]
77
77
  has_fields
78
78
  exportable
79
79
  sortable by: ["name ASC", "amount DESC", "amount*probability DESC", "probability DESC", "closes_on ASC", "created_at DESC", "updated_at DESC"], default: "created_at DESC"
@@ -28,9 +28,9 @@ class CustomFieldPair < CustomField
28
28
  pair = params.delete('pair')
29
29
  base_params = fields.delete_if { |k, _v| !%w[field_group_id label as].include?(k) }
30
30
  field1 = CustomFieldPair.find(params['id'])
31
- field1.update_attributes(base_params.merge(pair['0']))
31
+ field1.update(base_params.merge(pair['0']))
32
32
  field2 = field1.paired_with
33
- field2.update_attributes(base_params.merge(pair['1']).merge('required' => field1.required, 'disabled' => field1.disabled))
33
+ field2.update(base_params.merge(pair['1']).merge('required' => field1.required, 'disabled' => field1.disabled))
34
34
  [field1, field2]
35
35
  end
36
36
 
@@ -24,7 +24,7 @@ class OpportunityObserver < ActiveRecord::Observer
24
24
  if original.stage != "won" && item.stage == "won" # :other to :won -- add to total campaign revenue.
25
25
  update_campaign_revenue(item.campaign, item.amount.to_f - item.discount.to_f)
26
26
  item.update_attribute(:probability, 100) # Set probability to 100% if won
27
- return log_activity(item, :won)
27
+ log_activity(item, :won)
28
28
  elsif original.stage == "won" && item.stage != "won" # :won to :other -- substract from total campaign revenue.
29
29
  update_campaign_revenue(original.campaign, -(original.amount.to_f - original.discount.to_f))
30
30
  elsif original.stage != "lost" && item.stage == "lost"
@@ -28,7 +28,7 @@
28
28
  class Address < ActiveRecord::Base
29
29
  belongs_to :addressable, polymorphic: true
30
30
 
31
- has_paper_trail class_name: 'Version', meta: { related: :addressable }
31
+ has_paper_trail versions: { class_name: 'Version' }, meta: { related: :addressable }
32
32
 
33
33
  scope :business, -> { where("address_type='Business'") }
34
34
  scope :billing, -> { where("address_type='Billing'") }
@@ -28,7 +28,7 @@ class Comment < ActiveRecord::Base
28
28
  scope :created_by, ->(user) { where(user_id: user.id) }
29
29
 
30
30
  validates_presence_of :user, :commentable, :comment
31
- has_paper_trail class_name: 'Version', meta: { related: :commentable },
31
+ has_paper_trail versions: { class_name: 'Version' }, meta: { related: :commentable },
32
32
  ignore: [:state]
33
33
 
34
34
  before_create :subscribe_mentioned_users
@@ -33,7 +33,7 @@ class Email < ActiveRecord::Base
33
33
  belongs_to :mediator, polymorphic: true, optional: true # TODO: Is this really optional?
34
34
  belongs_to :user, optional: true # TODO: Is this really optional?
35
35
 
36
- has_paper_trail class_name: 'Version', meta: { related: :mediator },
36
+ has_paper_trail versions: { class_name: 'Version' }, meta: { related: :mediator },
37
37
  ignore: [:state]
38
38
 
39
39
  def expanded?
@@ -107,7 +107,7 @@ class Task < ActiveRecord::Base
107
107
  }
108
108
 
109
109
  acts_as_commentable
110
- has_paper_trail class_name: 'Version', meta: { related: :asset },
110
+ has_paper_trail versions: { class_name: 'Version' }, meta: { related: :asset },
111
111
  ignore: [:subscribed_users]
112
112
  has_fields
113
113
  exportable
@@ -26,6 +26,7 @@
26
26
  # `config/settings.default.yml`, and settings in the database table have the highest priority.
27
27
 
28
28
  class Setting < ActiveRecord::Base
29
+ validates :name, presence: true, allow_blank: false
29
30
  serialize :value
30
31
 
31
32
  # Use class variables for cache and yaml settings.
@@ -69,9 +70,10 @@ class Setting < ActiveRecord::Base
69
70
  # Set setting value
70
71
  #-------------------------------------------------------------------
71
72
  def []=(name, value)
72
- return nil unless database_and_table_exists?
73
+ raise ArgumentError, "name cannot be blank" if name.blank?
73
74
 
74
- setting = find_by_name(name.to_s) || new(name: name)
75
+ return nil unless database_and_table_exists?
76
+ setting = find_by_name(name.to_s) || new(name: name.to_s)
75
77
  setting.value = value
76
78
  setting.save
77
79
  cache[name] = value
@@ -28,7 +28,7 @@ class Preference < ActiveRecord::Base
28
28
  return cached_prefs[name.to_s] if cached_prefs.key?(name.to_s)
29
29
 
30
30
  cached_prefs[name.to_s] = if user.present? && pref = Preference.find_by_name_and_user_id(name.to_s, user.id)
31
- Marshal.load(Base64.decode64(pref.value))
31
+ JSON.parse(Base64.decode64(pref.value), symbolize_name: true)
32
32
  end
33
33
  end
34
34
 
@@ -36,7 +36,7 @@ class Preference < ActiveRecord::Base
36
36
  def []=(name, value)
37
37
  return super(name, value) if name.to_s == "user_id" # set the value of belongs_to
38
38
 
39
- encoded = Base64.encode64(Marshal.dump(value))
39
+ encoded = Base64.encode64(value.to_json)
40
40
  if pref = Preference.find_by(name: name.to_s, user_id: user.id)
41
41
  pref.update_attribute(:value, encoded)
42
42
  else
@@ -65,7 +65,7 @@ class User < ActiveRecord::Base
65
65
  has_many :lists
66
66
  has_and_belongs_to_many :groups
67
67
 
68
- has_paper_trail class_name: 'Version', ignore: [:last_sign_in_at]
68
+ has_paper_trail versions: { class_name: 'Version' }, ignore: [:last_sign_in_at]
69
69
 
70
70
  scope :by_id, -> { order('id DESC') }
71
71
  # TODO: /home/clockwerx/.rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/gems/activerecord-5.2.3/lib/active_record/scoping/named.rb:175:in `scope': You tried to define a scope named "without" on the model "User", but ActiveRecord::Relation already defined an instance method with the same name. (ArgumentError)
@@ -93,7 +93,7 @@ class User < ActiveRecord::Base
93
93
  validates :username,
94
94
  uniqueness: { message: :username_taken, case_sensitive: false },
95
95
  presence: { message: :missing_username },
96
- format: { with: /[a-z0-9_-]+/i }
96
+ format: { with: /\A[a-z0-9_-]+\z/i }
97
97
  validates :password,
98
98
  presence: { if: :password_required? },
99
99
  confirmation: true
@@ -14,8 +14,8 @@
14
14
 
15
15
  .buttonbar
16
16
  - if Setting.compound_address
17
- = f.submit t(:save_account), onclick: "crm.clear_all_hints()"
17
+ = f.submit t(:save_account), onclick: "crm.clear_all_hints()", class: 'btn btn-primary'
18
18
  - else
19
- = f.submit t(:save_account)
19
+ = f.submit t(:save_account), class: 'btn btn-primary'
20
20
  #{t :or}
21
21
  = link_to_cancel edit_account_path(@account)
@@ -14,8 +14,8 @@
14
14
 
15
15
  .buttonbar
16
16
  - if Setting.compound_address
17
- = f.submit t(:create_account), onclick: "crm.clear_all_hints()"
17
+ = f.submit t(:create_account), onclick: "crm.clear_all_hints()", class: 'btn btn-primary'
18
18
  - else
19
- = f.submit t(:create_account)
19
+ = f.submit t(:create_account), class: 'btn btn-primary'
20
20
  #{t :or}
21
21
  = link_to_cancel new_account_path
@@ -1,13 +1,13 @@
1
1
  .filters#filters
2
2
  .caption= t :account_categories
3
3
  - Setting.account_category.each do |key|
4
- .check_box{style: "border-bottom: 1px silver dotted;"}
4
+ .check_box
5
5
  %div{style: "float:right;"}
6
6
  = @account_category_total[key]
7
7
  = account_category_checkbox(key, @account_category_total[key])
8
8
  = label_tag(key, t(key))
9
9
 
10
- .check_box{style: "border-bottom: 1px silver dotted;"}
10
+ .check_box
11
11
  %div{style: "float:right;"}
12
12
  = @account_category_total[:other]
13
13
  = account_category_checkbox("other", @account_category_total[:other])
@@ -3,7 +3,6 @@
3
3
  - pipeline = @account.opportunities.pipeline.map(&:weighted_amount).compact.sum
4
4
 
5
5
  .panel#summary
6
- %h4= h(@account.name)
7
6
  - if @account.website
8
7
  %div
9
8
  %b= link_to(truncate(@account.website, length: 30), @account.website.to_url, :"data-popup" => true, title: t(:open_in_window, @account.website))
@@ -25,24 +24,25 @@
25
24
  %div= render "shared/address_show", asset: @account, type: 'shipping', title: :shipping_address
26
25
 
27
26
  .caption #{t :account_summary}
28
- %li
29
- %dt= pipeline != 0.0 ? number_to_currency(pipeline, precision: 0) : t(:n_a)
30
- %tt #{t :pipeline}:
31
- %li
32
- %dt= won != 0.0 ? number_to_currency(won, precision: 0) : t(:n_a)
33
- %tt #{t :won}:
34
- %li
35
- %dt= lost != 0.0 ? number_to_currency(lost, precision: 0) : t(:n_a)
36
- %tt #{t :lost}:
37
- %li
38
- %dt= @account.category ? t(@account.category) : t(:other)
39
- %tt #{t :category}:
40
- %li
41
- %span{style: "float:right;"}= stars_for(@account)
42
- %tt #{t :rating}:
43
- %li
44
- %dt= @account.assigned_to ? truncate(@account.assignee.full_name, length: 16) : nil
45
- %tt #{t :assigned_to}:
27
+ %dl
28
+ %li
29
+ %dt= display_value(pipeline)
30
+ %tt #{t :pipeline}:
31
+ %li
32
+ %dt= display_value(won)
33
+ %tt #{t :won}:
34
+ %li
35
+ %dt= display_value(lost)
36
+ %tt #{t :lost}:
37
+ %li
38
+ %dt= @account.category ? t(@account.category) : t(:other)
39
+ %tt #{t :category}:
40
+ %li
41
+ %span{style: "float:right;"}= stars_for(@account)
42
+ %tt #{t :rating}:
43
+ %li
44
+ %dt= display_assigned(@account)
45
+ %tt #{t :assigned_to}:
46
46
 
47
47
  - unless @account.background_info.blank?
48
48
  .caption #{t :background_info}
@@ -8,10 +8,9 @@
8
8
  $('##{create_id}').slideUp(250);
9
9
  $('##{entity_name.pluralize}').prepend('#{ j render(partial: entity_name, collection: [ @entity ]) }');
10
10
  $('##{dom_id(@entity)}').effect("highlight", { duration:1500 });
11
- = refresh_sidebar(:index, :filters)
11
+ = refresh_sidebar(:index)
12
12
  $('#paginate').html('#{ j render(partial: "shared/paginate_with_per_page") }');
13
13
  crm.flick('empty', 'remove');
14
14
  - else
15
15
  $('##{create_id}').html('#{ j render(partial: "new") }');
16
- $('##{create_id}').effect("shake", { duration:250, distance: 6 });
17
16
  $('#new_#{entity_name} input[type!=hidden]').first().focus();
@@ -2,5 +2,5 @@
2
2
  - @entity = instance_variable_get("@#{entity_name}")
3
3
 
4
4
  $('##{dom_id(@entity)}').css('background-color', '#ffe4e1').slideUp(250);
5
- = refresh_sidebar(:index, :filters)
5
+ = refresh_sidebar(:index)
6
6
  $('#paginate').replaceWith('#{ j render(partial: "shared/paginate_with_per_page") }');
@@ -7,6 +7,7 @@
7
7
  = styles_for :task, :contact, :opportunity
8
8
 
9
9
  = render 'accounts/title_bar', account: @account
10
+ = render 'accounts/sidebar_show', account: @account
10
11
 
11
12
  %div#edit_account{ hidden }
12
13
 
@@ -6,14 +6,13 @@
6
6
  - if called_from_landing_page?
7
7
  crm.flip_form('edit_#{entity_name}');
8
8
  crm.set_title('edit_#{entity_name}', '#{j @entity.name}');
9
- = refresh_sidebar(:show, :summary)
9
+ = refresh_sidebar(:show)
10
10
  - else
11
11
  $('##{id}').replaceWith('#{ j render(partial: entity_name, collection: [ @entity ]) }');
12
12
  $('##{id}').effect("highlight", { duration:1500 });
13
- = refresh_sidebar(:index, :filters)
13
+ = refresh_sidebar(:index)
14
14
  - else
15
15
  $('##{id}').html('#{ j render(partial: "edit") }');
16
- $('##{id}').effect("shake", { duration:250, distance: 6 });
17
16
  $('##{dom_id(@entity, :edit)} input[type!=hidden]').first().focus();
18
17
 
19
18
  = hook(:entity_update, self, {entity: @entity})
@@ -11,4 +11,3 @@
11
11
 
12
12
  - else
13
13
  $('##{id}').html('#{ j render(partial: "new") }');
14
- $('##{id}').effect("shake", { duration:250, distance: 6 });
@@ -3,7 +3,6 @@
3
3
  - if @field_group.destroyed?
4
4
  $('##{id}').css('background-color', '#ffe4e1').slideUp(250);
5
5
  - else
6
- $('##{id}').effect("shake", { distance:5 }, 250);
7
6
  $('#flash').html('#{ j t(:msg_cant_delete_field_group) }');
8
7
  crm.flash('warning');
9
8
  - flash[:warning] = nil
@@ -5,4 +5,3 @@
5
5
  crm.set_title('#{dom_id(@field_group)}', '#{field_group_subtitle(@field_group)}');
6
6
  - else
7
7
  $('##{id}').html('#{ j render(partial: 'edit') }');
8
- $('##{id}').effect('shake', { distance:5 }, 250);
@@ -13,5 +13,4 @@
13
13
  $('##{container_id}').sortable('reset');
14
14
  - else
15
15
  $('##{id}').html('#{ j render(partial: "admin/fields/form", locals: {field_group_id: @field.field_group_id}) }')
16
- $('##{id}').effect("shake", { duration:250, distance: 6 });
17
16
  $('##{id}').find('input[type="text"]').first().focus()
@@ -2,7 +2,6 @@
2
2
  - if @field.destroyed?
3
3
  $('li##{id}').css('background-color', '#ffe4e1').slideUp(250);
4
4
  - else
5
- $('li##{id}').effect("shake", { duration:250, distance: 6});
6
5
  $('#flash').html('#{ j flash[:warning] }')
7
6
  $(crm.flash('warning'))
8
7
  - flash[:warning] = nil
@@ -9,5 +9,4 @@
9
9
  - else
10
10
  html = $.parseHTML('#{ j(render partial: "form") }');
11
11
  $('##{id}').find('.edit_field').replaceWith( html );
12
- $('##{id}').effect("shake", { duration:250, distance: 6 });
13
12
  $('##{id}').find('input[type="text"]').first().focus();
@@ -6,5 +6,4 @@
6
6
  $('##{dom_id(@group)}').effect("highlight", { duration:1500 });
7
7
  - else
8
8
  $('#create_group').html('#{ j render(partial: "new") }');
9
- $('#create_group').effect("shake", { duration:250, distance: 6 });
10
9
  $('#group_name').focus();
@@ -3,7 +3,6 @@
3
3
  $('##{id}').css('background-color', '#ffe4e1').slideUp(250);
4
4
  - else
5
5
  crm.flick("#{dom_id(@group, :confirm)}, 'remove')");
6
- $('##{id}').effect("shake", { duration:250, distance: 6 });
7
6
  $('#flash').html('#{ j flash[:warning] }');
8
7
  crm.flash('warning');
9
8
  - flash[:warning] = nil
@@ -5,5 +5,4 @@
5
5
  $('##{id}').effect("highlight", { duration:1000 });
6
6
  - else
7
7
  $('##{id}').html('#{j render(partial: "edit") }')
8
- $('##{id}').effect("shake", { duration:250, distance: 6 });
9
8
  $('#group_name').focus()
@@ -7,5 +7,4 @@
7
7
  crm.flick('empty', 'remove');
8
8
  - else
9
9
  $('#create_tag').html('#{ j render(partial: 'new') }');
10
- $('#create_tag').effect("shake", { duration:250, distance: 6 });
11
10
  $('#tag_name').focus();
@@ -4,7 +4,6 @@
4
4
  $('##{id}').css('background-color', '#ffe4e1').slideUp(250);
5
5
  - else
6
6
  crm.flick("#{dom_id(@tag, :confirm)}, 'remove')");
7
- $('##{id}').effect("shake", { duration:250, distance: 6 });
8
7
  $('#flash').html('#{ j flash[:warning] }');
9
8
  crm.flash('warning');
10
9
  - flash[:warning] = nil
@@ -5,5 +5,4 @@
5
5
  $('##{id}').effect('highlight', { duration: 1000 });
6
6
  - else
7
7
  $('##{id}').html('#{ j render(partial: "edit") }');
8
- $('##{id}').effect("shake", { duration:250, distance: 6 });
9
8
  $('#tag_tagname').focus();
@@ -6,5 +6,4 @@
6
6
  $('##{dom_id(@user)}').effect("highlight", { duration:1500 });
7
7
  - else
8
8
  $('#create_user').html('#{ j (render partial: "new") }');
9
- $('#create_user').effect("shake", { duration:250, distance: 6 });
10
9
  $('#group_username').focus();
@@ -4,7 +4,6 @@
4
4
  $('##{id}').css('background-color', '#ffe4e1').slideUp(250).remove();
5
5
  - else
6
6
  crm.flick('#{dom_id(@user, :confirm)}', 'remove');
7
- $('##{id}').effect('shake', { duration:250, distance: 6 });
8
7
  $('#flash').html('#{j flash[:warning]}');
9
8
  crm.flash('warning');
10
9
  - flash[:warning] = nil
@@ -5,5 +5,4 @@
5
5
  $('##{id}').effect("highlight", { duration:1000 });
6
6
  - else
7
7
  $('##{id}').html('#{j render(partial: "edit") }')
8
- $('##{id}').effect("shake", { duration:250, distance: 6 });
9
8
  $('#user_username').focus()
@@ -13,6 +13,6 @@
13
13
  = hook(:entity_form, self, {f: f, entity: @campaign})
14
14
 
15
15
  .buttonbar
16
- = f.submit t(:save_campaign)
16
+ = f.submit t(:save_campaign), class: 'btn btn-primary'
17
17
  #{t :or}
18
18
  = link_to_cancel edit_campaign_path(@campaign)
@@ -13,6 +13,6 @@
13
13
  = hook(:entity_form, self, {f: f, entity: @campaign})
14
14
 
15
15
  .buttonbar
16
- = f.submit t(:create_campaign)
16
+ = f.submit t(:create_campaign), class: 'btn btn-primary'
17
17
  #{t :or}
18
18
  = link_to_cancel new_campaign_path
@@ -1,13 +1,13 @@
1
1
  .filters#filters
2
2
  .caption= t :campaign_statuses
3
3
  - Setting.campaign_status.each do |key|
4
- .check_box{style: "border-bottom: 1px silver dotted;"}
4
+ .check_box
5
5
  %div{style: "float:right;"}
6
6
  = @campaign_status_total[key]
7
7
  = campaign_status_checkbox(key, @campaign_status_total[key])
8
8
  = label_tag(key, t(key))
9
9
 
10
- .check_box{style: "border-bottom: 1px silver dotted;"}
10
+ .check_box
11
11
  %div{style: "float:right;"}
12
12
  = @campaign_status_total[:other]
13
13
  = campaign_status_checkbox("other", @campaign_status_total[:other])