fat_free_crm 0.15.0.beta.2 → 0.15.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 (225) hide show
  1. checksums.yaml +4 -4
  2. data/.docker/nginx/sites-enabled/ffcrm.conf +8 -0
  3. data/.gitignore +2 -0
  4. data/.rubocop.yml +4 -1
  5. data/.rubocop_todo.yml +190 -89
  6. data/.travis.yml +10 -3
  7. data/CHANGELOG.md +27 -0
  8. data/{CONTRIBUTORS → CONTRIBUTORS.md} +2 -1
  9. data/Dockerfile +15 -13
  10. data/Gemfile +7 -4
  11. data/Gemfile.lock +200 -167
  12. data/README.md +4 -2
  13. data/app/assets/javascripts/crm_sortable.js.coffee +5 -0
  14. data/app/assets/javascripts/timeago.js.coffee +5 -0
  15. data/app/assets/stylesheets/about.css.scss +5 -0
  16. data/app/assets/stylesheets/common.scss +2 -1
  17. data/app/controllers/admin/fields_controller.rb +1 -1
  18. data/app/controllers/admin/groups_controller.rb +5 -1
  19. data/app/controllers/admin/tags_controller.rb +1 -1
  20. data/app/controllers/admin/users_controller.rb +10 -6
  21. data/app/controllers/application_controller.rb +13 -13
  22. data/app/controllers/authentications_controller.rb +2 -2
  23. data/app/controllers/comments_controller.rb +3 -2
  24. data/app/controllers/entities/contacts_controller.rb +9 -7
  25. data/app/controllers/entities/opportunities_controller.rb +1 -1
  26. data/app/controllers/entities_controller.rb +4 -4
  27. data/app/controllers/home_controller.rb +9 -9
  28. data/app/controllers/passwords_controller.rb +1 -1
  29. data/app/controllers/tasks_controller.rb +2 -1
  30. data/app/controllers/users_controller.rb +4 -2
  31. data/app/helpers/application_helper.rb +9 -9
  32. data/app/helpers/javascript_helper.rb +5 -0
  33. data/app/helpers/opportunities_helper.rb +1 -1
  34. data/app/helpers/remote_link_pagination_helper.rb +5 -0
  35. data/app/inputs/date_pair_input.rb +1 -1
  36. data/app/models/entities/account.rb +2 -2
  37. data/app/models/entities/account_contact.rb +1 -1
  38. data/app/models/entities/campaign.rb +3 -3
  39. data/app/models/entities/contact.rb +3 -3
  40. data/app/models/entities/lead.rb +2 -2
  41. data/app/models/entities/opportunity.rb +3 -3
  42. data/app/models/fields/custom_field.rb +1 -1
  43. data/app/models/fields/custom_field_pair.rb +2 -2
  44. data/app/models/fields/field.rb +1 -1
  45. data/app/models/polymorphic/address.rb +2 -2
  46. data/app/models/polymorphic/avatar.rb +4 -2
  47. data/app/models/polymorphic/email.rb +8 -6
  48. data/app/models/polymorphic/task.rb +3 -1
  49. data/app/models/polymorphic/version.rb +3 -3
  50. data/app/models/users/ability.rb +1 -1
  51. data/app/models/users/permission.rb +2 -0
  52. data/app/models/users/user.rb +2 -2
  53. data/app/views/accounts/index.js.haml +1 -1
  54. data/app/views/admin/fields/_sort_by.html.haml +1 -1
  55. data/app/views/admin/users/_user.html.haml +1 -1
  56. data/app/views/campaigns/index.js.haml +1 -1
  57. data/app/views/contacts/index.js.haml +1 -1
  58. data/app/views/entities/_basic_search.html.haml +1 -1
  59. data/app/views/home/_duration_menu.html.haml +1 -1
  60. data/app/views/home/_events_menu.html.haml +1 -1
  61. data/app/views/home/_users_menu.html.haml +1 -1
  62. data/app/views/layouts/application.html.haml +1 -1
  63. data/app/views/leads/index.js.haml +1 -1
  64. data/app/views/opportunities/index.js.haml +1 -1
  65. data/app/views/shared/_naming.html.haml +1 -1
  66. data/app/views/users/_languages.html.haml +1 -1
  67. data/config/application.rb +2 -3
  68. data/config/boot.rb +2 -0
  69. data/config/deploy.example.rb +1 -1
  70. data/config/environments/development.rb +2 -0
  71. data/config/environments/production.rb +1 -1
  72. data/config/environments/test.rb +2 -2
  73. data/config/initializers/assets.rb +6 -1
  74. data/config/initializers/backtrace_silencers.rb +5 -0
  75. data/config/initializers/constants.rb +1 -1
  76. data/config/initializers/cookies_serializer.rb +5 -0
  77. data/config/initializers/custom_field_ransack_translations.rb +5 -0
  78. data/config/initializers/filter_parameter_logging.rb +5 -0
  79. data/config/initializers/inflections.rb +5 -0
  80. data/config/initializers/paper_trail.rb +5 -0
  81. data/config/initializers/ransack.rb +3 -3
  82. data/config/initializers/session_store.rb +5 -0
  83. data/config/initializers/wrap_parameters.rb +5 -0
  84. data/config/locales/et.yml +207 -0
  85. data/config/locales/et_fat_free_crm.yml +928 -0
  86. data/config/locales/pt-BR_ransack.yml +81 -0
  87. data/config/locales/th.rb +1 -1
  88. data/config/routes.rb +18 -18
  89. data/db/migrate/20100928030598_create_sessions.rb +1 -1
  90. data/db/migrate/20100928030599_create_users.rb +2 -2
  91. data/db/migrate/20100928030600_create_openid_tables.rb +1 -1
  92. data/db/migrate/20100928030601_create_accounts.rb +2 -2
  93. data/db/migrate/20100928030602_create_permissions.rb +1 -1
  94. data/db/migrate/20100928030603_create_settings.rb +1 -1
  95. data/db/migrate/20100928030604_create_preferences.rb +2 -2
  96. data/db/migrate/20100928030605_create_campaigns.rb +2 -2
  97. data/db/migrate/20100928030606_create_leads.rb +2 -2
  98. data/db/migrate/20100928030607_create_contacts.rb +2 -2
  99. data/db/migrate/20100928030608_create_opportunities.rb +2 -2
  100. data/db/migrate/20100928030609_create_account_contacts.rb +1 -1
  101. data/db/migrate/20100928030610_create_account_opportunities.rb +1 -1
  102. data/db/migrate/20100928030611_create_contact_opportunities.rb +1 -1
  103. data/db/migrate/20100928030612_create_tasks.rb +2 -2
  104. data/db/migrate/20100928030613_create_comments.rb +1 -1
  105. data/db/migrate/20100928030614_create_activities.rb +1 -1
  106. data/db/migrate/20100928030615_create_avatars.rb +1 -1
  107. data/db/migrate/20100928030616_rename_remember_token.rb +1 -1
  108. data/db/migrate/20100928030617_drop_openid_tables.rb +1 -1
  109. data/db/migrate/20100928030618_add_admin_to_users.rb +1 -1
  110. data/db/migrate/20100928030619_add_suspended_to_users.rb +1 -1
  111. data/db/migrate/20100928030620_remove_uuid.rb +2 -2
  112. data/db/migrate/20100928030621_add_email_to_accounts.rb +1 -1
  113. data/db/migrate/20100928030622_add_background_info_to_models.rb +1 -1
  114. data/db/migrate/20100928030623_create_addresses.rb +2 -2
  115. data/db/migrate/20100928030624_add_index_on_permissions.rb +3 -3
  116. data/db/migrate/20100928030625_create_emails.rb +2 -2
  117. data/db/migrate/20100928030626_add_state_to_timeline_objects.rb +1 -1
  118. data/db/migrate/20100928030627_acts_as_taggable_on_migration.rb +2 -2
  119. data/db/migrate/20101221123456_add_single_access_token_to_users.rb +1 -1
  120. data/db/migrate/20101221345678_add_rating_and_category_to_accounts.rb +1 -1
  121. data/db/migrate/20110719082054_add_skype_to_contacts_and_leads.rb +1 -1
  122. data/db/migrate/20111101083437_create_fields.rb +1 -1
  123. data/db/migrate/20111101090312_create_field_groups.rb +1 -1
  124. data/db/migrate/20111116091952_add_field_groups_tag_id.rb +1 -1
  125. data/db/migrate/20111117041311_change_fields_collection_to_text.rb +1 -1
  126. data/db/migrate/20111201030535_add_field_groups_klass_name.rb +2 -2
  127. data/db/migrate/20120121054235_create_lists.rb +1 -1
  128. data/db/migrate/20120216031616_create_versions.rb +3 -3
  129. data/db/migrate/20120216042541_is_paranoid_to_paper_trail.rb +1 -1
  130. data/db/migrate/20120220233724_add_versions_object_changes.rb +1 -1
  131. data/db/migrate/20120224073107_remove_default_value_and_clear_settings.rb +1 -1
  132. data/db/migrate/20120309070209_add_versions_related.rb +1 -1
  133. data/db/migrate/20120314080441_add_subscribed_users_to_entities.rb +2 -2
  134. data/db/migrate/20120316045804_activities_to_versions.rb +1 -1
  135. data/db/migrate/20120405080727_change_subscribed_users_to_set.rb +1 -1
  136. data/db/migrate/20120405080742_change_further_subscribed_users_to_set.rb +2 -2
  137. data/db/migrate/20120406082136_create_groups.rb +2 -2
  138. data/db/migrate/20120413034923_add_index_on_versions_item_type.rb +1 -1
  139. data/db/migrate/20120510025219_add_not_null_constraints_for_timestamp_columns.rb +3 -3
  140. data/db/migrate/20120528102124_increase_length_of_version_events.rb +1 -1
  141. data/db/migrate/20120801032706_add_pair_id_to_fields.rb +1 -1
  142. data/db/migrate/20121003063155_add_settings_to_custom_fields.rb +1 -1
  143. data/db/migrate/20121221033947_fix_country_mapping.rb +1 -1
  144. data/db/migrate/20131207033244_add_user_id_to_lists.rb +1 -1
  145. data/db/migrate/20140916011927_add_created_at_index_on_versions.rb +1 -1
  146. data/db/migrate/20140916012922_add_indexes_to_model_associations.rb +3 -3
  147. data/db/migrate/20141126031837_increase_email_to254_chars.rb +1 -1
  148. data/db/migrate/20141230021159_add_transaction_id_column_to_versions.rb +1 -1
  149. data/db/migrate/20141230205453_add_missing_unique_indices.acts_as_taggable_on_engine.rb +4 -4
  150. data/db/migrate/20141230205454_add_taggings_counter_cache_to_tags.acts_as_taggable_on_engine.rb +1 -1
  151. data/db/migrate/20141230205455_add_missing_taggable_index.acts_as_taggable_on_engine.rb +3 -3
  152. data/db/migrate/20150123060900_convert_radio_to_radio_buttons.rb +1 -1
  153. data/db/migrate/20150227123054_remove_last_request_at_from_users.rb +1 -1
  154. data/db/migrate/20150427131956_create_index_related_type.rb +3 -3
  155. data/db/migrate/20160511053730_add_account_contacts_index.rb +2 -2
  156. data/docker-compose.yml +17 -13
  157. data/fat_free_crm.gemspec +4 -6
  158. data/lib/development_tasks/license.rake +12 -9
  159. data/lib/fat_free_crm/callback.rb +4 -4
  160. data/lib/fat_free_crm/engine.rb +2 -2
  161. data/lib/fat_free_crm/exportable.rb +2 -2
  162. data/lib/fat_free_crm/fields.rb +2 -4
  163. data/lib/fat_free_crm/gem_ext.rb +0 -1
  164. data/lib/fat_free_crm/gem_ext/rake/task.rb +2 -0
  165. data/lib/fat_free_crm/mail_processor/base.rb +4 -4
  166. data/lib/fat_free_crm/mail_processor/dropbox.rb +1 -1
  167. data/lib/fat_free_crm/permissions.rb +5 -5
  168. data/lib/fat_free_crm/sortable.rb +1 -1
  169. data/lib/fat_free_crm/version.rb +1 -1
  170. data/lib/gravatar_image_tag.rb +1 -0
  171. data/lib/tasks/ffcrm/demo.rake +2 -2
  172. data/spec/controllers/admin/users_controller_spec.rb +25 -25
  173. data/spec/controllers/authentications_controller_spec.rb +9 -9
  174. data/spec/controllers/comments_controller_spec.rb +15 -15
  175. data/spec/controllers/emails_controller_spec.rb +2 -2
  176. data/spec/controllers/entities/accounts_controller_spec.rb +46 -46
  177. data/spec/controllers/entities/campaigns_controller_spec.rb +46 -46
  178. data/spec/controllers/entities/contacts_controller_spec.rb +55 -55
  179. data/spec/controllers/entities/leads_controller_spec.rb +85 -85
  180. data/spec/controllers/entities/opportunities_controller_spec.rb +74 -74
  181. data/spec/controllers/home_controller_spec.rb +11 -11
  182. data/spec/controllers/passwords_controller_spec.rb +2 -2
  183. data/spec/controllers/tasks_controller_spec.rb +37 -37
  184. data/spec/controllers/users_controller_spec.rb +31 -31
  185. data/spec/factories/campaign_factories.rb +1 -1
  186. data/spec/factories/contact_factories.rb +1 -1
  187. data/spec/factories/field_factories.rb +1 -1
  188. data/spec/factories/lead_factories.rb +2 -2
  189. data/spec/factories/opportunity_factories.rb +3 -3
  190. data/spec/factories/shared_factories.rb +1 -1
  191. data/spec/factories/task_factories.rb +1 -1
  192. data/spec/features/support/browser.rb +9 -1
  193. data/spec/lib/fields_spec.rb +2 -2
  194. data/spec/lib/permissions_spec.rb +38 -6
  195. data/spec/lib/view_factory_spec.rb +2 -2
  196. data/spec/models/fields/custom_field_spec.rb +3 -3
  197. data/spec/models/observers/entity_observer_spec.rb +1 -1
  198. data/spec/models/polymorphic/version_spec.rb +11 -11
  199. data/spec/models/users/abilities/user_ability_spec.rb +8 -3
  200. data/spec/models/users/permission_spec.rb +8 -0
  201. data/spec/models/users/user_spec.rb +1 -1
  202. data/spec/shared/controllers.rb +10 -10
  203. data/spec/spec_helper.rb +1 -1
  204. data/spec/views/accounts/index.haml_spec.rb +1 -1
  205. data/spec/views/accounts/update.js.haml_spec.rb +2 -2
  206. data/spec/views/admin/users/update.js.haml_spec.rb +2 -2
  207. data/spec/views/application/auto_complete.haml_spec.rb +1 -1
  208. data/spec/views/campaigns/index.haml_spec.rb +1 -1
  209. data/spec/views/campaigns/update.js.haml_spec.rb +2 -2
  210. data/spec/views/contacts/index.haml_spec.rb +1 -1
  211. data/spec/views/contacts/update.js.haml_spec.rb +2 -2
  212. data/spec/views/home/options.js.haml_spec.rb +1 -1
  213. data/spec/views/leads/index.haml_spec.rb +1 -1
  214. data/spec/views/leads/promote.js.haml_spec.rb +2 -2
  215. data/spec/views/leads/update.js.haml_spec.rb +2 -2
  216. data/spec/views/opportunities/index.haml_spec.rb +1 -1
  217. data/spec/views/opportunities/update.js.haml_spec.rb +1 -1
  218. data/spec/views/tasks/_edit.haml_spec.rb +2 -2
  219. data/spec/views/tasks/create.js.haml_spec.rb +2 -2
  220. data/spec/views/tasks/edit.js.haml_spec.rb +1 -1
  221. data/spec/views/users/change_password.js.haml_spec.rb +2 -2
  222. data/spec/views/users/update.js.haml_spec.rb +2 -2
  223. data/spec/views/users/upload_avatar.js.haml_spec.rb +2 -2
  224. metadata +17 -20
  225. data/lib/fat_free_crm/gem_ext/active_record/schema_dumper.rb +0 -27
@@ -16,14 +16,14 @@ module ApplicationHelper
16
16
 
17
17
  #----------------------------------------------------------------------------
18
18
  def tabless_layout?
19
- %w(authentications passwords).include?(controller.controller_name) ||
20
- ((controller.controller_name == "users") && %w(create new).include?(controller.action_name))
19
+ %w[authentications passwords].include?(controller.controller_name) ||
20
+ ((controller.controller_name == "users") && %w[create new].include?(controller.action_name))
21
21
  end
22
22
 
23
23
  # Show existing flash or embed hidden paragraph ready for flash[:notice]
24
24
  #----------------------------------------------------------------------------
25
25
  def show_flash(options = { sticky: false })
26
- [:error, :warning, :info, :notice].each do |type|
26
+ %i[error warning info notice].each do |type|
27
27
  if flash[type]
28
28
  html = content_tag(:div, h(flash[type]), id: "flash")
29
29
  flash[type] = nil
@@ -175,7 +175,7 @@ module ApplicationHelper
175
175
 
176
176
  #----------------------------------------------------------------------------
177
177
  def jumpbox(current)
178
- tabs = [:campaigns, :accounts, :leads, :contacts, :opportunities]
178
+ tabs = %i[campaigns accounts leads contacts opportunities]
179
179
  current = tabs.first unless tabs.include?(current)
180
180
  tabs.map do |tab|
181
181
  link_to_function(t("tab_#{tab}"), "crm.jumper('#{tab}')", "html-data" => tab, class: (tab == current ? 'selected' : ''))
@@ -252,7 +252,7 @@ module ApplicationHelper
252
252
  # Display web presence mini-icons for Contact or Lead.
253
253
  #----------------------------------------------------------------------------
254
254
  def web_presence_icons(person)
255
- [:blog, :linkedin, :facebook, :twitter, :skype].map do |site|
255
+ %i[blog linkedin facebook twitter skype].map do |site|
256
256
  url = person.send(site)
257
257
  unless url.blank?
258
258
  if site == :skype
@@ -302,7 +302,7 @@ module ApplicationHelper
302
302
  #----------------------------------------------------------------------------
303
303
  def get_browser_timezone_offset
304
304
  unless session[:timezone_offset]
305
- "$.get('#{timezone_path}', {offset: (new Date()).getTimezoneOffset()});"
305
+ raw "$.get('#{timezone_path}', {offset: (new Date()).getTimezoneOffset()});"
306
306
  end
307
307
  end
308
308
 
@@ -368,15 +368,15 @@ module ApplicationHelper
368
368
  url_params[:view] = @view unless @view.blank? # tasks
369
369
  url_params[:id] = params[:id] unless params[:id].blank?
370
370
 
371
- exports = %w(xls csv).map do |format|
371
+ exports = %w[xls csv].map do |format|
372
372
  link_to(format.upcase, url_params.merge(format: format), title: I18n.t(:"to_#{format}")) unless action.to_s == "show"
373
373
  end
374
374
 
375
- feeds = %w(rss atom).map do |format|
375
+ feeds = %w[rss atom].map do |format|
376
376
  link_to(format.upcase, url_params.merge(format: format, authentication_credentials: token), title: I18n.t(:"to_#{format}"))
377
377
  end
378
378
 
379
- links = %w(perm).map do |format|
379
+ links = ['perm'].map do |format|
380
380
  link_to(format.upcase, url_params, title: I18n.t(:"to_#{format}"))
381
381
  end
382
382
 
@@ -1,3 +1,8 @@
1
+ # Copyright (c) 2008-2013 Michael Dvorkin and contributors.
2
+ #
3
+ # Fat Free CRM is freely distributable under the terms of MIT license.
4
+ # See MIT-LICENSE file or http://www.opensource.org/licenses/mit-license.php
5
+ #------------------------------------------------------------------------------
1
6
  # Copied from prototype-rails which is no longer maintained
2
7
 
3
8
  module JavascriptHelper
@@ -17,7 +17,7 @@ module OpportunitiesHelper
17
17
  amount = []
18
18
  summary << (opportunity.stage ? t(opportunity.stage) : t(:other))
19
19
  summary << number_to_currency(opportunity.weighted_amount, precision: 0)
20
- unless %w(won lost).include?(opportunity.stage)
20
+ unless %w[won lost].include?(opportunity.stage)
21
21
  amount << number_to_currency(opportunity.amount || 0, precision: 0)
22
22
  amount << (opportunity.discount ? t(:discount_number, number_to_currency(opportunity.discount, precision: 0)) : t(:no_discount))
23
23
  amount << t(:probability_number, (opportunity.probability || 0).to_s + '%')
@@ -1,3 +1,8 @@
1
+ # Copyright (c) 2008-2013 Michael Dvorkin and contributors.
2
+ #
3
+ # Fat Free CRM is freely distributable under the terms of MIT license.
4
+ # See MIT-LICENSE file or http://www.opensource.org/licenses/mit-license.php
5
+ #------------------------------------------------------------------------------
1
6
  module RemoteLinkPaginationHelper
2
7
  class LinkRenderer < WillPaginate::ActionView::LinkRenderer
3
8
  def link(text, target, attributes = {})
@@ -15,7 +15,7 @@ class DatePairInput < SimpleForm::Inputs::Base
15
15
  [field1, field2].compact.each do |field|
16
16
  out << '<div>'.html_safe
17
17
  label = field == field1 ? I18n.t('pair.start') : I18n.t('pair.end')
18
- [:required, :disabled].each { |k| input_html_options.delete(k) } # ensure these come from field not default options
18
+ %i[required disabled].each { |k| input_html_options.delete(k) } # ensure these come from field not default options
19
19
  input_html_options.merge!(field.input_options)
20
20
  input_html_options[:value] = value(field)
21
21
  out << "<label#{' class="req"' if input_html_options[:required]}>#{label}</label>".html_safe
@@ -49,7 +49,7 @@ class Account < ActiveRecord::Base
49
49
  scope :created_by, ->(user) { where(user_id: user.id) }
50
50
  scope :assigned_to, ->(user) { where(assigned_to: user.id) }
51
51
 
52
- scope :text_search, ->(query) { search('name_or_email_cont' => query).result }
52
+ scope :text_search, ->(query) { ransack('name_or_email_cont' => query).result }
53
53
 
54
54
  scope :visible_on_dashboard, ->(user) {
55
55
  # Show accounts which either belong to the user and are unassigned, or are assigned to the user
@@ -67,7 +67,7 @@ class Account < ActiveRecord::Base
67
67
  exportable
68
68
  sortable by: ["name ASC", "rating DESC", "created_at DESC", "updated_at DESC"], default: "created_at DESC"
69
69
 
70
- has_ransackable_associations %w(contacts opportunities tags activities emails addresses comments tasks)
70
+ has_ransackable_associations %w[contacts opportunities tags activities emails addresses comments tasks]
71
71
  ransack_can_autocomplete
72
72
 
73
73
  validates_presence_of :name, message: :missing_account_name
@@ -20,7 +20,7 @@ class AccountContact < ActiveRecord::Base
20
20
  belongs_to :contact
21
21
 
22
22
  has_paper_trail class_name: 'Version', meta: { related: :contact },
23
- ignore: [:id, :created_at, :updated_at, :contact_id]
23
+ ignore: %i[id created_at updated_at contact_id]
24
24
 
25
25
  validates_presence_of :account_id
26
26
 
@@ -45,7 +45,7 @@ class Campaign < ActiveRecord::Base
45
45
  scope :created_by, ->(user) { where(user_id: user.id) }
46
46
  scope :assigned_to, ->(user) { where(assigned_to: user.id) }
47
47
 
48
- scope :text_search, ->(query) { search('name_cont' => query).result }
48
+ scope :text_search, ->(query) { ransack('name_cont' => query).result }
49
49
 
50
50
  uses_user_permissions
51
51
  acts_as_commentable
@@ -56,11 +56,11 @@ class Campaign < ActiveRecord::Base
56
56
  exportable
57
57
  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"
58
58
 
59
- has_ransackable_associations %w(leads opportunities tags activities emails comments tasks)
59
+ has_ransackable_associations %w[leads opportunities tags activities emails comments tasks]
60
60
  ransack_can_autocomplete
61
61
 
62
62
  validates_presence_of :name, message: :missing_campaign_name
63
- validates_uniqueness_of :name, scope: [:user_id, :deleted_at]
63
+ validates_uniqueness_of :name, scope: %i[user_id deleted_at]
64
64
  validate :start_and_end_dates
65
65
  validate :users_for_shared_access
66
66
  validates :status, inclusion: { in: proc { Setting.unroll(:campaign_status).map { |s| s.last.to_s } } }, allow_blank: true
@@ -52,7 +52,7 @@ class Contact < ActiveRecord::Base
52
52
 
53
53
  delegate :campaign, to: :lead, allow_nil: true
54
54
 
55
- has_ransackable_associations %w(account opportunities tags activities emails addresses comments tasks)
55
+ has_ransackable_associations %w[account opportunities tags activities emails addresses comments tasks]
56
56
  ransack_can_autocomplete
57
57
 
58
58
  serialize :subscribed_users, Set
@@ -162,7 +162,7 @@ class Contact < ActiveRecord::Base
162
162
  assigned_to: params[:account][:assigned_to],
163
163
  access: params[:access]
164
164
  }
165
- %w(first_name last_name title source email alt_email phone mobile blog linkedin facebook twitter skype do_not_call background_info).each do |name|
165
+ %w[first_name last_name title source email alt_email phone mobile blog linkedin facebook twitter skype do_not_call background_info].each do |name|
166
166
  attributes[name] = model.send(name.intern)
167
167
  end
168
168
 
@@ -205,7 +205,7 @@ class Contact < ActiveRecord::Base
205
205
  #----------------------------------------------------------------------------
206
206
  def save_account(params)
207
207
  account_params = params[:account]
208
- if account_params[:id] == "" || account_params[:name] == ""
208
+ if !account_params || account_params[:id] == "" || account_params[:name] == ""
209
209
  self.account = nil
210
210
  else
211
211
  self.account = Account.create_or_select_for(self, account_params)
@@ -58,7 +58,7 @@ class Lead < ActiveRecord::Base
58
58
  scope :created_by, ->(user) { where(user_id: user.id) }
59
59
  scope :assigned_to, ->(user) { where(assigned_to: user.id) }
60
60
 
61
- scope :text_search, ->(query) { search('first_name_or_last_name_or_company_or_email_cont' => query).result }
61
+ scope :text_search, ->(query) { ransack('first_name_or_last_name_or_company_or_email_cont' => query).result }
62
62
 
63
63
  uses_user_permissions
64
64
  acts_as_commentable
@@ -69,7 +69,7 @@ class Lead < ActiveRecord::Base
69
69
  exportable
70
70
  sortable by: ["first_name ASC", "last_name ASC", "company ASC", "rating DESC", "created_at DESC", "updated_at DESC"], default: "created_at DESC"
71
71
 
72
- has_ransackable_associations %w(contact campaign tasks tags activities emails addresses comments)
72
+ has_ransackable_associations %w[contact campaign tasks tags activities emails addresses comments]
73
73
  ransack_can_autocomplete
74
74
 
75
75
  validates_presence_of :first_name, message: :missing_first_name, if: -> { Setting.require_first_names }
@@ -54,7 +54,7 @@ class Opportunity < ActiveRecord::Base
54
54
  if query =~ /\A\d+\z/
55
55
  where('upper(name) LIKE upper(:name) OR opportunities.id = :id', name: "%#{query}%", id: query)
56
56
  else
57
- search('name_cont' => query).result
57
+ ransack('name_cont' => query).result
58
58
  end
59
59
  }
60
60
 
@@ -75,11 +75,11 @@ class Opportunity < ActiveRecord::Base
75
75
  exportable
76
76
  sortable by: ["name ASC", "amount DESC", "amount*probability DESC", "probability DESC", "closes_on ASC", "created_at DESC", "updated_at DESC"], default: "created_at DESC"
77
77
 
78
- has_ransackable_associations %w(account contacts tags campaign activities emails comments)
78
+ has_ransackable_associations %w[account contacts tags campaign activities emails comments]
79
79
  ransack_can_autocomplete
80
80
 
81
81
  validates_presence_of :name, message: :missing_opportunity_name
82
- validates_numericality_of [:probability, :amount, :discount], allow_nil: true
82
+ validates_numericality_of %i[probability amount discount], allow_nil: true
83
83
  validate :users_for_shared_access
84
84
  validates :stage, inclusion: { in: proc { Setting.unroll(:opportunity_stage).map { |s| s.last.to_s } } }, allow_blank: true
85
85
 
@@ -54,7 +54,7 @@ class CustomField < Field
54
54
  after_create :add_ransack_translation
55
55
 
56
56
  SAFE_DB_TRANSITIONS = {
57
- any: [%w(date time timestamp), %w(integer float)],
57
+ any: [%w[date time timestamp], %w[integer float]],
58
58
  one: { 'string' => 'text' }
59
59
  }
60
60
 
@@ -12,7 +12,7 @@ class CustomFieldPair < CustomField
12
12
  fields = params['field']
13
13
  as = params['field']['as']
14
14
  pair = params.delete('pair')
15
- base_params = fields.delete_if { |k, _v| !%w(field_group_id label as).include?(k) }
15
+ base_params = fields.delete_if { |k, _v| !%w[field_group_id label as].include?(k) }
16
16
  klass = ("custom_field_" + as.gsub('pair', '_pair')).classify.constantize
17
17
  field1 = klass.create(base_params.merge(pair['0']))
18
18
  field2 = klass.create(base_params.merge(pair['1']).merge('pair_id' => field1.id, 'required' => field1.required, 'disabled' => field1.disabled))
@@ -24,7 +24,7 @@ class CustomFieldPair < CustomField
24
24
  def self.update_pair(params)
25
25
  fields = params['field']
26
26
  pair = params.delete('pair')
27
- base_params = fields.delete_if { |k, _v| !%w(field_group_id label as).include?(k) }
27
+ base_params = fields.delete_if { |k, _v| !%w[field_group_id label as].include?(k) }
28
28
  field1 = CustomFieldPair.find(params['id'])
29
29
  field1.update_attributes(base_params.merge(pair['0']))
30
30
  field2 = field1.paired_with
@@ -69,7 +69,7 @@ class Field < ActiveRecord::Base
69
69
  def input_options
70
70
  input_html = {}
71
71
  attributes.reject do |k, v|
72
- !%w(as collection disabled label placeholder required maxlength).include?(k) || v.blank?
72
+ !%w[as collection disabled label placeholder required maxlength].include?(k) || v.blank?
73
73
  end.symbolize_keys.merge(input_html)
74
74
  end
75
75
 
@@ -36,7 +36,7 @@ class Address < ActiveRecord::Base
36
36
  #----------------------------------------------------------------------------
37
37
  def blank?
38
38
  if Setting.compound_address
39
- %w(street1 street2 city state zipcode country).all? { |attr| send(attr).blank? }
39
+ %w[street1 street2 city state zipcode country].all? { |attr| send(attr).blank? }
40
40
  else
41
41
  full_address.blank?
42
42
  end
@@ -50,7 +50,7 @@ class Address < ActiveRecord::Base
50
50
  # accepts_nested_attributes_for :business_address, :allow_destroy => true, :reject_if => proc {|attributes| Address.reject_address(attributes)}
51
51
  def self.reject_address(attributes)
52
52
  exists = attributes['id'].present?
53
- empty = %w(street1 street2 city state zipcode country full_address).map { |name| attributes[name].blank? }.all?
53
+ empty = %w[street1 street2 city state zipcode country full_address].map { |name| attributes[name].blank? }.all?
54
54
  attributes[:_destroy] = 1 if exists && empty
55
55
  (!exists && empty)
56
56
  end
@@ -36,7 +36,7 @@ class Avatar < ActiveRecord::Base
36
36
  end
37
37
  has_attached_file :image, styles: STYLES.dup, url: "/avatars/:entity_type/:id/:style_:filename", default_url: "/assets/avatar.jpg"
38
38
  validates_attachment :image, presence: true,
39
- content_type: { content_type: %w(image/jpeg image/jpg image/png image/gif) }
39
+ content_type: { content_type: ['image/jpeg', 'image/jpg', 'image/png', 'image/gif'] }
40
40
 
41
41
  # Convert STYLE symbols to 'w x h' format for Gravatar and Rails
42
42
  # e.g. Avatar.size_from_style(:size => :large) -> '75x75'
@@ -44,7 +44,9 @@ class Avatar < ActiveRecord::Base
44
44
  #----------------------------------------------------------------------------
45
45
  def self.size_from_style!(options)
46
46
  if options[:width] && options[:height]
47
- options[:size] = [:width, :height].map { |d| options[d] }.join("x")
47
+ options[:size] = %i[width height].map { |d| options[d] }.join("x")
48
+ options.delete(:width)
49
+ options.delete(:height)
48
50
  elsif Avatar::STYLES.keys.include?(options[:size])
49
51
  options[:size] = Avatar::STYLES[options[:size]].sub(/\#\z/, '')
50
52
  end
@@ -46,14 +46,16 @@ class Email < ActiveRecord::Base
46
46
  super
47
47
  end
48
48
 
49
- def body_with_textile
50
- if defined?(RedCloth)
51
- RedCloth.new(body_without_textile).to_html
52
- else
53
- body_without_textile.to_s.gsub("\n", "<br/>")
49
+ module BodyWithTextile
50
+ def body
51
+ if defined?(RedCloth)
52
+ RedCloth.new(super).to_html
53
+ else
54
+ super.to_s.gsub("\n", "<br/>")
55
+ end
54
56
  end
55
57
  end
56
- alias_method_chain :body, :textile
58
+ prepend BodyWithTextile
57
59
 
58
60
  ActiveSupport.run_load_hooks(:fat_free_crm_email, self)
59
61
  end
@@ -26,8 +26,10 @@
26
26
  #
27
27
 
28
28
  class Task < ActiveRecord::Base
29
+ include ActiveModel::Serializers::Xml
30
+
29
31
  attr_accessor :calendar
30
- ALLOWED_VIEWS = %w(pending assigned completed)
32
+ ALLOWED_VIEWS = %w[pending assigned completed]
31
33
 
32
34
  belongs_to :user
33
35
  belongs_to :assignee, class_name: "User", foreign_key: :assigned_to
@@ -6,9 +6,9 @@
6
6
  require 'paper_trail'
7
7
 
8
8
  class Version < PaperTrail::Version
9
- ASSETS = %w(all tasks campaigns leads accounts contacts opportunities comments emails)
10
- EVENTS = %w(all_events create view update destroy)
11
- DURATION = %w(one_hour one_day two_days one_week two_weeks one_month)
9
+ ASSETS = %w[all tasks campaigns leads accounts contacts opportunities comments emails]
10
+ EVENTS = %w[all_events create view update destroy]
11
+ DURATION = %w[one_hour one_day two_days one_week two_weeks one_month]
12
12
 
13
13
  belongs_to :related, polymorphic: true
14
14
  belongs_to :user, foreign_key: :whodunnit
@@ -50,7 +50,7 @@ class Ability
50
50
  can :manage, klass, id: asset_ids
51
51
  end
52
52
  end
53
- end # if user.admin?
53
+ end
54
54
 
55
55
  end
56
56
  end
@@ -23,5 +23,7 @@ class Permission < ActiveRecord::Base
23
23
  validates_presence_of :user_id, unless: :group_id?
24
24
  validates_presence_of :group_id, unless: :user_id?
25
25
 
26
+ validates_uniqueness_of :user_id, scope: %i[group_id asset_id asset_type]
27
+
26
28
  ActiveSupport.run_load_hooks(:fat_free_crm_permission, self)
27
29
  end
@@ -159,7 +159,7 @@ class User < ActiveRecord::Base
159
159
  # Prevent deleting a user unless she has no artifacts left.
160
160
  #----------------------------------------------------------------------------
161
161
  def has_related_assets?
162
- sum = %w(Account Campaign Lead Contact Opportunity Comment Task).detect do |asset|
162
+ sum = %w[Account Campaign Lead Contact Opportunity Comment Task].detect do |asset|
163
163
  klass = asset.constantize
164
164
 
165
165
  asset != "Comment" && klass.assigned_to(self).exists? || klass.created_by(self).exists?
@@ -177,7 +177,7 @@ class User < ActiveRecord::Base
177
177
  end
178
178
 
179
179
  def can_signup?
180
- [:allowed, :needs_approval].include? Setting.user_signup
180
+ %i[allowed needs_approval].include? Setting.user_signup
181
181
  end
182
182
  end
183
183
 
@@ -8,4 +8,4 @@
8
8
 
9
9
  $('#paginate').html('#{ j render(:partial => "shared/paginate_with_per_page") }');
10
10
  $('#export').html('#{ j render(:partial => "shared/export") }');
11
- $('#search_results_count').html('#{ j render(:text => t('search_results_count', :count => @search_results_count)) }');
11
+ $('#search_results_count').html('#{ j render(:plain => t('search_results_count', :count => @search_results_count)) }');
@@ -6,5 +6,5 @@
6
6
  trigger : "#sort_by",
7
7
  fade : 500,
8
8
  appear : 500,
9
- menu_items: [ #{sort_by_menu_items.join(",")} ]
9
+ menu_items: [ #{raw sort_by_menu_items.join(",")} ]
10
10
  });
@@ -9,7 +9,7 @@
9
9
  - else
10
10
  .strip.active #{t :user_active}
11
11
 
12
- = avatar_for(user, size: :medium, width: '30', height: '30')
12
+ = avatar_for(user, width: '30', height: '30')
13
13
 
14
14
  .indentfull
15
15
  .tools
@@ -8,4 +8,4 @@
8
8
 
9
9
  $('#paginate').html('#{ j render(:partial => "shared/paginate_with_per_page") }');
10
10
  $('#export').html('#{ j render(:partial => "shared/export") }');
11
- $('#search_results_count').html('#{ j render(:text => t('search_results_count', :count => @search_results_count)) }');
11
+ $('#search_results_count').html('#{ j render(:plain => t('search_results_count', :count => @search_results_count)) }');
@@ -8,4 +8,4 @@
8
8
 
9
9
  $('#paginate').html('#{ j render(:partial => "shared/paginate_with_per_page") }');
10
10
  $('#export').html('#{ j render(:partial => "shared/export") }');
11
- $('#search_results_count').html('#{ j render(:text => t('search_results_count', :count => @search_results_count)) }');
11
+ $('#search_results_count').html('#{ j render(:plain => t('search_results_count', :count => @search_results_count)) }');
@@ -20,5 +20,5 @@
20
20
  trigger : "#sort_by",
21
21
  fade : 500,
22
22
  appear : 500,
23
- menu_items: [ #{sort_by_menu_items.join(",")} ]
23
+ menu_items: [ #{raw sort_by_menu_items.join(",")} ]
24
24
  });
@@ -4,5 +4,5 @@
4
4
  fade : 500,
5
5
  appear : 500,
6
6
  width : 165,
7
- menu_items: [ #{sort_by_duration.join(",")} ]
7
+ menu_items: [ #{raw sort_by_duration.join(",")} ]
8
8
  });