fat_free_crm 0.18.1 → 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 (385) 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 +80 -173
  6. data/.travis.yml +17 -20
  7. data/CHANGELOG.md +63 -3
  8. data/CONTRIBUTORS.md +1 -0
  9. data/Dockerfile +45 -14
  10. data/Gemfile +21 -11
  11. data/Gemfile.lock +315 -263
  12. data/Procfile +1 -1
  13. data/README.md +6 -5
  14. data/Rakefile +1 -1
  15. data/SECURITY.md +11 -0
  16. data/app/assets/javascripts/application.js.erb +3 -0
  17. data/app/assets/stylesheets/application.css.erb +1 -0
  18. data/app/assets/stylesheets/base.scss +9 -2
  19. data/app/assets/stylesheets/bootstrap-custom.scss +206 -0
  20. data/app/assets/stylesheets/common.scss +88 -68
  21. data/app/assets/stylesheets/header.scss +0 -8
  22. data/app/controllers/admin/application_controller.rb +1 -1
  23. data/app/controllers/admin/field_groups_controller.rb +2 -4
  24. data/app/controllers/admin/fields_controller.rb +1 -1
  25. data/app/controllers/admin/groups_controller.rb +1 -1
  26. data/app/controllers/admin/tags_controller.rb +3 -5
  27. data/app/controllers/admin/users_controller.rb +7 -9
  28. data/app/controllers/application_controller.rb +21 -45
  29. data/app/controllers/comments_controller.rb +3 -6
  30. data/{config/initializers/authlogic.rb → app/controllers/confirmations_controller.rb} +4 -2
  31. data/app/controllers/emails_controller.rb +0 -2
  32. data/app/controllers/entities/accounts_controller.rb +2 -4
  33. data/app/controllers/entities/campaigns_controller.rb +2 -4
  34. data/app/controllers/entities/contacts_controller.rb +4 -24
  35. data/app/controllers/entities/leads_controller.rb +7 -10
  36. data/app/controllers/entities/opportunities_controller.rb +4 -14
  37. data/app/controllers/entities_controller.rb +21 -7
  38. data/app/controllers/home_controller.rb +5 -5
  39. data/app/controllers/lists_controller.rb +1 -1
  40. data/app/controllers/passwords_controller.rb +3 -59
  41. data/{spec/features/support/maintain_sessions.rb → app/controllers/registrations_controller.rb} +12 -5
  42. data/{lib/development_tasks/gem.rake → app/controllers/sessions_controller.rb} +6 -6
  43. data/app/controllers/tasks_controller.rb +11 -20
  44. data/app/controllers/users_controller.rb +11 -30
  45. data/app/helpers/accounts_helper.rb +12 -0
  46. data/app/helpers/admin/users_helper.rb +1 -1
  47. data/app/helpers/application_helper.rb +30 -37
  48. data/app/helpers/campaigns_helper.rb +1 -1
  49. data/app/helpers/contacts_helper.rb +1 -3
  50. data/app/helpers/opportunities_helper.rb +4 -12
  51. data/app/helpers/tasks_helper.rb +3 -3
  52. data/app/helpers/users_helper.rb +1 -3
  53. data/{config/initializers/paper_trail.rb → app/mailers/devise_mailer.rb} +5 -1
  54. data/app/mailers/user_mailer.rb +0 -9
  55. data/app/models/entities/account.rb +11 -11
  56. data/app/models/entities/account_contact.rb +1 -1
  57. data/app/models/entities/account_opportunity.rb +1 -1
  58. data/app/models/entities/campaign.rb +5 -7
  59. data/app/models/entities/contact.rb +25 -13
  60. data/app/models/entities/lead.rb +8 -8
  61. data/app/models/entities/opportunity.rb +8 -10
  62. data/app/models/fields/custom_field.rb +1 -0
  63. data/app/models/fields/custom_field_date_pair.rb +2 -0
  64. data/app/models/fields/custom_field_pair.rb +2 -2
  65. data/app/models/fields/field.rb +1 -3
  66. data/app/models/list.rb +1 -1
  67. data/app/models/observers/entity_observer.rb +3 -7
  68. data/app/models/observers/lead_observer.rb +2 -4
  69. data/app/models/observers/opportunity_observer.rb +3 -5
  70. data/app/models/observers/task_observer.rb +1 -1
  71. data/app/models/polymorphic/address.rb +1 -1
  72. data/app/models/polymorphic/comment.rb +1 -1
  73. data/app/models/polymorphic/email.rb +3 -3
  74. data/app/models/polymorphic/task.rb +14 -10
  75. data/app/models/polymorphic/version.rb +3 -2
  76. data/app/models/setting.rb +5 -1
  77. data/app/models/users/permission.rb +3 -3
  78. data/app/models/users/preference.rb +4 -3
  79. data/app/models/users/user.rb +67 -42
  80. data/app/views/accounts/_edit.html.haml +2 -2
  81. data/app/views/accounts/_new.html.haml +2 -2
  82. data/app/views/accounts/_sidebar_index.html.haml +2 -2
  83. data/app/views/accounts/_sidebar_show.html.haml +19 -19
  84. data/app/views/accounts/_top_section.html.haml +1 -1
  85. data/app/views/accounts/create.js.haml +1 -2
  86. data/app/views/accounts/destroy.js.haml +1 -1
  87. data/app/views/accounts/edit.js.haml +1 -1
  88. data/app/views/accounts/show.html.haml +1 -0
  89. data/app/views/accounts/update.js.haml +4 -5
  90. data/app/views/admin/field_groups/create.js.haml +0 -1
  91. data/app/views/admin/field_groups/destroy.js.haml +0 -1
  92. data/app/views/admin/field_groups/update.js.haml +0 -1
  93. data/app/views/admin/fields/create.js.haml +0 -1
  94. data/app/views/admin/fields/destroy.js.haml +0 -1
  95. data/app/views/admin/fields/update.js.haml +0 -1
  96. data/app/views/admin/groups/create.js.haml +0 -1
  97. data/app/views/admin/groups/destroy.js.haml +0 -1
  98. data/app/views/admin/groups/update.js.haml +0 -1
  99. data/app/views/admin/tags/create.js.haml +0 -1
  100. data/app/views/admin/tags/destroy.js.haml +0 -1
  101. data/app/views/admin/tags/update.js.haml +0 -1
  102. data/app/views/admin/users/_user.html.haml +4 -4
  103. data/app/views/admin/users/create.js.haml +0 -1
  104. data/app/views/admin/users/destroy.js.haml +0 -1
  105. data/app/views/admin/users/update.js.haml +0 -1
  106. data/app/views/campaigns/_edit.html.haml +1 -1
  107. data/app/views/campaigns/_new.html.haml +1 -1
  108. data/app/views/campaigns/_sidebar_index.html.haml +2 -2
  109. data/app/views/campaigns/_sidebar_show.html.haml +68 -67
  110. data/app/views/campaigns/create.js.haml +1 -2
  111. data/app/views/campaigns/destroy.js.haml +1 -1
  112. data/app/views/campaigns/show.html.haml +1 -0
  113. data/app/views/campaigns/update.js.haml +3 -4
  114. data/app/views/comments/_edit.html.haml +1 -1
  115. data/app/views/comments/_new.html.haml +1 -1
  116. data/app/views/comments/update.js.haml +0 -1
  117. data/app/views/contacts/_edit.html.haml +1 -1
  118. data/app/views/contacts/_index_brief.html.haml +1 -1
  119. data/app/views/contacts/_index_full.html.haml +1 -1
  120. data/app/views/contacts/_index_long.html.haml +1 -1
  121. data/app/views/contacts/_new.html.haml +1 -1
  122. data/app/views/contacts/_sidebar_show.html.haml +18 -18
  123. data/app/views/contacts/create.js.haml +0 -1
  124. data/app/views/contacts/destroy.js.haml +1 -1
  125. data/app/views/contacts/show.html.haml +1 -0
  126. data/app/views/contacts/update.js.haml +2 -3
  127. data/app/views/devise/confirmations/new.html.haml +9 -0
  128. data/app/views/devise/mailer/confirmation_instructions.html.haml +4 -0
  129. data/app/views/devise/mailer/password_change.html.haml +3 -0
  130. data/app/views/devise/mailer/reset_password_instructions.html.haml +6 -0
  131. data/app/views/devise/passwords/edit.html.haml +18 -0
  132. data/app/views/devise/passwords/new.html.haml +10 -0
  133. data/app/views/devise/registrations/new.html.haml +21 -0
  134. data/app/views/devise/sessions/new.html.haml +32 -0
  135. data/app/views/entities/_title_bar.html.haml +1 -1
  136. data/app/views/entities/attach.js.haml +2 -2
  137. data/app/views/entities/discard.js.haml +2 -2
  138. data/app/views/home/_opportunity.html.haml +1 -1
  139. data/app/views/home/_task.html.haml +1 -1
  140. data/app/views/home/index.xls.builder +51 -0
  141. data/app/views/layouts/_about.html.haml +5 -5
  142. data/app/views/layouts/_header.html.haml +3 -3
  143. data/app/views/layouts/_sidebar.html.haml +2 -2
  144. data/app/views/layouts/_tabbed.html.haml +14 -11
  145. data/app/views/layouts/admin/_header.html.haml +1 -1
  146. data/app/views/layouts/application.html.haml +2 -2
  147. data/app/views/leads/_edit.html.haml +2 -2
  148. data/app/views/leads/_new.html.haml +2 -2
  149. data/app/views/leads/_sidebar_index.html.haml +2 -2
  150. data/app/views/leads/_sidebar_show.html.haml +30 -30
  151. data/app/views/leads/create.js.haml +2 -3
  152. data/app/views/leads/destroy.js.haml +2 -2
  153. data/app/views/leads/promote.js.haml +3 -4
  154. data/app/views/leads/reject.js.haml +3 -3
  155. data/app/views/leads/show.html.haml +1 -1
  156. data/app/views/leads/update.js.haml +4 -5
  157. data/app/views/lists/create.js.haml +0 -1
  158. data/app/views/opportunities/_edit.html.haml +1 -1
  159. data/app/views/opportunities/_index_brief.html.haml +1 -1
  160. data/app/views/opportunities/_index_long.html.haml +1 -1
  161. data/app/views/opportunities/_new.html.haml +1 -1
  162. data/app/views/opportunities/_sidebar_index.html.haml +2 -2
  163. data/app/views/opportunities/_sidebar_show.html.haml +47 -46
  164. data/app/views/opportunities/create.js.haml +3 -4
  165. data/app/views/opportunities/destroy.js.haml +3 -3
  166. data/app/views/opportunities/show.html.haml +1 -0
  167. data/app/views/opportunities/update.js.haml +5 -6
  168. data/app/views/shared/_address.html.haml +5 -5
  169. data/app/views/shared/_empty.html.haml +1 -1
  170. data/app/views/shared/_inline_styles.html.haml +0 -26
  171. data/app/views/shared/_paginate_with_per_page.html.haml +1 -0
  172. data/app/views/tasks/_assigned.html.haml +1 -1
  173. data/app/views/tasks/_completed.html.haml +1 -1
  174. data/app/views/tasks/_edit.html.haml +1 -1
  175. data/app/views/tasks/_new.html.haml +1 -1
  176. data/app/views/tasks/_pending.html.haml +1 -1
  177. data/app/views/tasks/_related.html.haml +1 -1
  178. data/app/views/tasks/_selector.html.haml +7 -8
  179. data/app/views/tasks/_sidebar_index.html.haml +2 -2
  180. data/app/views/tasks/_title.html.haml +1 -1
  181. data/app/views/tasks/complete.js.haml +1 -1
  182. data/app/views/tasks/create.js.haml +1 -2
  183. data/app/views/tasks/destroy.js.haml +1 -1
  184. data/app/views/tasks/uncomplete.js.haml +1 -2
  185. data/app/views/tasks/update.js.haml +0 -1
  186. data/app/views/users/_avatar.html.haml +1 -1
  187. data/app/views/users/change_password.js.haml +0 -1
  188. data/app/views/users/update.js.haml +0 -1
  189. data/app/views/users/upload_avatar.js.haml +0 -1
  190. data/app/views/versions/_version_item.html.haml +1 -1
  191. data/bin/bundle +1 -1
  192. data/bin/rails +1 -1
  193. data/bin/setup +38 -0
  194. data/bin/update +33 -0
  195. data/bin/yarn +13 -0
  196. data/config/application.rb +8 -6
  197. data/config/boot.rb +1 -1
  198. data/config/brakeman.ignore +2 -2
  199. data/config/database.postgres.docker.yml +5 -5
  200. data/config/environment.rb +2 -2
  201. data/config/environments/development.rb +1 -0
  202. data/config/environments/test.rb +7 -0
  203. data/config/initializers/action_mailer.rb +1 -3
  204. data/config/initializers/application_controller_renderer.rb +9 -0
  205. data/config/initializers/assets.rb +6 -11
  206. data/config/initializers/backtrace_silencers.rb +0 -6
  207. data/config/initializers/content_security_policy.rb +31 -0
  208. data/config/initializers/cookies_serializer.rb +3 -6
  209. data/config/initializers/devise.rb +289 -0
  210. data/config/initializers/filter_parameter_logging.rb +0 -5
  211. data/config/initializers/gravatar.rb +0 -1
  212. data/config/initializers/inflections.rb +0 -6
  213. data/config/initializers/mime_types.rb +1 -9
  214. data/config/initializers/new_framework_defaults_6_0.rb +46 -0
  215. data/config/initializers/relative_url_root.rb +1 -3
  216. data/config/initializers/session_store.rb +1 -3
  217. data/config/initializers/simple_form.rb +135 -55
  218. data/config/initializers/simple_form_bootstrap.rb +433 -0
  219. data/config/initializers/wrap_parameters.rb +4 -9
  220. data/config/locales/fat_free_crm.cs.yml +86 -47
  221. data/config/locales/fat_free_crm.en-GB.yml +5 -5
  222. data/config/locales/fat_free_crm.en-US.yml +5 -5
  223. data/config/locales/fat_free_crm.fr.yml +1 -1
  224. data/config/locales/fat_free_crm.ru.yml +2 -0
  225. data/config/routes.rb +20 -9
  226. data/config/settings.default.yml +0 -41
  227. data/db/demo/users.yml +62 -81
  228. data/db/migrate/20100928030620_remove_uuid.rb +1 -2
  229. data/db/migrate/20120316045804_activities_to_versions.rb +1 -0
  230. data/db/migrate/20120510025219_add_not_null_constraints_for_timestamp_columns.rb +1 -0
  231. data/db/migrate/20180107082701_authlogic_to_devise.rb +58 -0
  232. data/db/migrate/20200806004152_add_pattern_to_fields.rb +7 -0
  233. data/db/migrate/20200806004459_add_html5_to_fields.rb +10 -0
  234. data/db/schema.rb +58 -48
  235. data/docker-compose.yml +10 -0
  236. data/fat_free_crm.gemspec +13 -15
  237. data/lib/development_tasks/license.rake +2 -2
  238. data/lib/fat_free_crm/callback.rb +2 -2
  239. data/lib/fat_free_crm/comment_extensions.rb +2 -4
  240. data/lib/fat_free_crm/core_ext/string.rb +1 -1
  241. data/lib/fat_free_crm/core_ext.rb +1 -1
  242. data/lib/fat_free_crm/engine.rb +1 -1
  243. data/lib/fat_free_crm/errors.rb +1 -1
  244. data/lib/fat_free_crm/export_csv.rb +1 -0
  245. data/lib/fat_free_crm/exportable.rb +1 -1
  246. data/lib/fat_free_crm/fields.rb +1 -1
  247. data/lib/fat_free_crm/gem_dependencies.rb +1 -1
  248. data/lib/fat_free_crm/gem_ext.rb +0 -1
  249. data/lib/fat_free_crm/i18n.rb +2 -2
  250. data/lib/fat_free_crm/mail_processor/base.rb +4 -10
  251. data/lib/fat_free_crm/mail_processor/dropbox.rb +5 -15
  252. data/lib/fat_free_crm/permissions.rb +7 -4
  253. data/lib/fat_free_crm/sortable.rb +1 -1
  254. data/lib/fat_free_crm/tabs.rb +2 -2
  255. data/lib/fat_free_crm/version.rb +2 -2
  256. data/lib/gravatar_image_tag.rb +8 -9
  257. data/lib/missing_translation_detector.rb +1 -0
  258. data/lib/tasks/ffcrm/missing_translations.rake +1 -0
  259. data/lib/tasks/ffcrm/preference_update.rake +19 -0
  260. data/lib/tasks/ffcrm/setup.rake +11 -2
  261. data/lib/tasks/ffcrm/update_data.rake +2 -2
  262. data/lib/templates/erb/scaffold/_form.html.erb +4 -2
  263. data/script/rails +2 -2
  264. data/spec/controllers/admin/users_controller_spec.rb +0 -56
  265. data/spec/controllers/comments_controller_spec.rb +12 -12
  266. data/spec/controllers/entities/campaigns_controller_spec.rb +1 -1
  267. data/spec/controllers/entities/contacts_controller_spec.rb +2 -1
  268. data/spec/controllers/entities/leads_controller_spec.rb +2 -2
  269. data/spec/controllers/entities/opportunities_controller_spec.rb +1 -1
  270. data/spec/controllers/entities_controller_spec.rb +5 -0
  271. data/spec/controllers/home_controller_spec.rb +8 -8
  272. data/spec/controllers/tasks_controller_spec.rb +6 -4
  273. data/spec/controllers/users_controller_spec.rb +41 -99
  274. data/spec/factories/account_factories.rb +5 -5
  275. data/spec/factories/campaign_factories.rb +3 -3
  276. data/spec/factories/contact_factories.rb +8 -8
  277. data/spec/factories/field_factories.rb +4 -3
  278. data/spec/factories/lead_factories.rb +5 -5
  279. data/spec/factories/list_factories.rb +2 -2
  280. data/spec/factories/opportunity_factories.rb +3 -3
  281. data/spec/factories/setting_factories.rb +2 -2
  282. data/spec/factories/shared_factories.rb +11 -9
  283. data/spec/factories/task_factories.rb +7 -7
  284. data/spec/factories/user_factories.rb +21 -21
  285. data/spec/features/acceptance_helper.rb +1 -1
  286. data/spec/features/accounts_spec.rb +2 -2
  287. data/spec/features/admin/groups_spec.rb +2 -2
  288. data/spec/features/admin/users_spec.rb +4 -2
  289. data/spec/features/campaigns_spec.rb +3 -3
  290. data/spec/features/contacts_spec.rb +3 -6
  291. data/spec/features/dashboard_spec.rb +1 -1
  292. data/spec/features/devise/sign_in_spec.rb +58 -0
  293. data/spec/features/devise/sign_up_spec.rb +36 -0
  294. data/spec/features/leads_spec.rb +12 -8
  295. data/spec/features/opportunities_overview_spec.rb +1 -1
  296. data/spec/features/opportunities_spec.rb +7 -7
  297. data/spec/features/support/browser.rb +8 -3
  298. data/spec/features/support/selector_helpers.rb +10 -1
  299. data/spec/features/tasks_spec.rb +7 -7
  300. data/spec/helpers/accounts_helper_spec.rb +57 -0
  301. data/spec/helpers/admin/field_groups_helper_spec.rb +1 -1
  302. data/spec/helpers/users_helper_spec.rb +4 -4
  303. data/spec/lib/comment_extensions_spec.rb +10 -4
  304. data/spec/lib/errors_spec.rb +2 -2
  305. data/spec/lib/mail_processor/dropbox_spec.rb +1 -1
  306. data/spec/lib/mail_processor/sample_emails/dropbox.rb +8 -8
  307. data/spec/lib/permissions_spec.rb +8 -3
  308. data/spec/mailers/devise_mailer_spec.rb +35 -0
  309. data/spec/mailers/user_mailer_spec.rb +0 -26
  310. data/spec/models/entities/account_spec.rb +27 -0
  311. data/spec/models/entities/contact_spec.rb +96 -1
  312. data/spec/models/entities/opportunity_spec.rb +2 -2
  313. data/spec/models/fields/custom_field_date_pair_spec.rb +4 -2
  314. data/spec/models/fields/custom_field_pair_spec.rb +2 -2
  315. data/spec/models/fields/custom_field_spec.rb +4 -2
  316. data/spec/models/observers/entity_observer_spec.rb +4 -4
  317. data/spec/models/polymorphic/task_spec.rb +11 -11
  318. data/spec/models/polymorphic/version_spec.rb +9 -9
  319. data/spec/models/users/preference_spec.rb +1 -1
  320. data/spec/models/users/user_spec.rb +22 -26
  321. data/spec/routing/users_routing_spec.rb +30 -8
  322. data/spec/shared/controllers.rb +3 -9
  323. data/spec/spec_helper.rb +10 -2
  324. data/spec/support/assert_select.rb +1 -0
  325. data/spec/support/devise_helpers.rb +28 -0
  326. data/spec/{features/support/helpers.rb → support/feature_helpers.rb} +10 -10
  327. data/spec/support/macros.rb +4 -1
  328. data/spec/views/accounts/_edit.haml_spec.rb +1 -1
  329. data/spec/views/accounts/_new.haml_spec.rb +1 -1
  330. data/spec/views/accounts/create.js.haml_spec.rb +1 -2
  331. data/spec/views/accounts/update.js.haml_spec.rb +2 -5
  332. data/spec/views/admin/field_groups/create.js.haml_spec.rb +0 -1
  333. data/spec/views/admin/field_groups/update.js.haml_spec.rb +0 -1
  334. data/spec/views/admin/users/create.js.haml_spec.rb +0 -1
  335. data/spec/views/admin/users/destroy.js.haml_spec.rb +0 -6
  336. data/spec/views/admin/users/update.js.haml_spec.rb +1 -2
  337. data/spec/views/campaigns/_edit.haml_spec.rb +1 -1
  338. data/spec/views/campaigns/_new.haml_spec.rb +1 -1
  339. data/spec/views/campaigns/create.js.haml_spec.rb +1 -2
  340. data/spec/views/campaigns/destroy.js.haml_spec.rb +0 -1
  341. data/spec/views/campaigns/edit.js.haml_spec.rb +2 -2
  342. data/spec/views/campaigns/index.js.haml_spec.rb +1 -1
  343. data/spec/views/campaigns/update.js.haml_spec.rb +3 -7
  344. data/spec/views/contacts/_edit.haml_spec.rb +1 -1
  345. data/spec/views/contacts/_new.haml_spec.rb +1 -1
  346. data/spec/views/contacts/create.js.haml_spec.rb +1 -2
  347. data/spec/views/contacts/edit.js.haml_spec.rb +1 -1
  348. data/spec/views/contacts/index.js.html_spec.rb +1 -1
  349. data/spec/views/contacts/update.js.haml_spec.rb +6 -10
  350. data/spec/views/home/index.haml_spec.rb +2 -2
  351. data/spec/views/leads/_edit.haml_spec.rb +1 -1
  352. data/spec/views/leads/_new.haml_spec.rb +1 -1
  353. data/spec/views/leads/create.js.haml_spec.rb +0 -3
  354. data/spec/views/leads/destroy.js.haml_spec.rb +0 -2
  355. data/spec/views/leads/promote.js.haml_spec.rb +3 -11
  356. data/spec/views/leads/reject.js.haml_spec.rb +0 -3
  357. data/spec/views/leads/update.js.haml_spec.rb +3 -11
  358. data/spec/views/opportunities/_edit.haml_spec.rb +1 -1
  359. data/spec/views/opportunities/_new.haml_spec.rb +1 -1
  360. data/spec/views/opportunities/create.js.haml_spec.rb +0 -2
  361. data/spec/views/opportunities/destroy.js.haml_spec.rb +0 -3
  362. data/spec/views/opportunities/update.js.haml_spec.rb +4 -12
  363. data/spec/views/tasks/_edit.haml_spec.rb +1 -1
  364. data/spec/views/tasks/complete.js.haml_spec.rb +0 -1
  365. data/spec/views/tasks/create.js.haml_spec.rb +0 -2
  366. data/spec/views/tasks/destroy.js.haml_spec.rb +0 -1
  367. data/spec/views/tasks/uncomplete.js.haml_spec.rb +0 -1
  368. data/spec/views/tasks/update.js.haml_spec.rb +1 -4
  369. data/spec/views/users/change_password.js.haml_spec.rb +1 -2
  370. data/spec/views/users/update.js.haml_spec.rb +1 -2
  371. data/spec/views/users/upload_avatar.js.haml_spec.rb +1 -2
  372. metadata +63 -77
  373. data/app/controllers/authentications_controller.rb +0 -53
  374. data/app/models/users/authentication.rb +0 -56
  375. data/app/views/authentications/new.html.haml +0 -19
  376. data/app/views/passwords/edit.html.haml +0 -15
  377. data/app/views/passwords/new.html.haml +0 -10
  378. data/app/views/user_mailer/password_reset_instructions.html.haml +0 -6
  379. data/app/views/users/new.html.haml +0 -19
  380. data/lib/fat_free_crm/gem_ext/simple_form/action_view_extensions/form_helper.rb +0 -26
  381. data/spec/controllers/authentications_controller_spec.rb +0 -150
  382. data/spec/controllers/passwords_controller_spec.rb +0 -32
  383. data/spec/models/users/authentication_spec.rb +0 -19
  384. data/spec/support/auth_macros.rb +0 -49
  385. data/spec/views/authentications/new.haml_spec.rb +0 -31
@@ -5,8 +5,15 @@
5
5
  # Fat Free CRM is freely distributable under the terms of MIT license.
6
6
  # See MIT-LICENSE file or http://www.opensource.org/licenses/mit-license.php
7
7
  #------------------------------------------------------------------------------
8
- #
9
- # Workaround for ActionDispatch::ClosedError
10
- # https://github.com/binarylogic/authlogic/issues/262#issuecomment-1804988
11
- #
12
- User.acts_as_authentic_config[:maintain_sessions] = false
8
+ class RegistrationsController < Devise::RegistrationsController
9
+ respond_to :html
10
+ append_view_path 'app/views/devise'
11
+
12
+ def edit
13
+ redirect_to profile_path
14
+ end
15
+
16
+ def after_inactive_sign_up_path_for(*)
17
+ new_user_session_path
18
+ end
19
+ end
@@ -5,11 +5,11 @@
5
5
  # Fat Free CRM is freely distributable under the terms of MIT license.
6
6
  # See MIT-LICENSE file or http://www.opensource.org/licenses/mit-license.php
7
7
  #------------------------------------------------------------------------------
8
- require 'rubygems/package_task'
8
+ class SessionsController < Devise::SessionsController
9
+ respond_to :html
10
+ append_view_path 'app/views/devise'
9
11
 
10
- Bundler::GemHelper.install_tasks
11
-
12
- gemspec = eval(File.read('fat_free_crm.gemspec'))
13
- Gem::PackageTask.new(gemspec) do |p|
14
- p.gem_spec = gemspec
12
+ def after_sign_out_path_for(*)
13
+ new_user_session_path
14
+ end
15
15
  end
@@ -6,7 +6,6 @@
6
6
  # See MIT-LICENSE file or http://www.opensource.org/licenses/mit-license.php
7
7
  #------------------------------------------------------------------------------
8
8
  class TasksController < ApplicationController
9
- before_action :require_user
10
9
  before_action :set_current_tab, only: %i[index show]
11
10
  before_action :update_sidebar, only: :index
12
11
 
@@ -59,9 +58,7 @@ class TasksController < ApplicationController
59
58
  @category = Setting.unroll(:task_category)
60
59
  @asset = @task.asset if @task.asset_id?
61
60
 
62
- if params[:previous].to_s =~ /(\d+)\z/
63
- @previous = Task.tracked_by(current_user).find_by_id(Regexp.last_match[1]) || Regexp.last_match[1].to_i
64
- end
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/
65
62
 
66
63
  respond_with(@task)
67
64
  end
@@ -93,12 +90,10 @@ class TasksController < ApplicationController
93
90
  end
94
91
 
95
92
  respond_with(@task) do |_format|
96
- if @task.update_attributes(task_params)
93
+ if @task.update(task_params)
97
94
  @task.bucket = @task.computed_bucket
98
95
  if called_from_index_page?
99
- if Task.bucket_empty?(@task_before_update.bucket, current_user, @view)
100
- @empty_bucket = @task_before_update.bucket
101
- end
96
+ @empty_bucket = @task_before_update.bucket if Task.bucket_empty?(@task_before_update.bucket, current_user, @view)
102
97
  update_sidebar
103
98
  end
104
99
  end
@@ -113,9 +108,7 @@ class TasksController < ApplicationController
113
108
  @task.destroy
114
109
 
115
110
  # Make sure bucket's div gets hidden if we're deleting last task in the bucket.
116
- if Task.bucket_empty?(params[:bucket], current_user, @view)
117
- @empty_bucket = params[:bucket]
118
- end
111
+ @empty_bucket = params[:bucket] if Task.bucket_empty?(params[:bucket], current_user, @view)
119
112
 
120
113
  update_sidebar if called_from_index_page?
121
114
  respond_with(@task)
@@ -125,12 +118,10 @@ class TasksController < ApplicationController
125
118
  #----------------------------------------------------------------------------
126
119
  def complete
127
120
  @task = Task.tracked_by(current_user).find(params[:id])
128
- @task&.update_attributes(completed_at: Time.now, completed_by: current_user.id)
121
+ @task&.update(completed_at: Time.now, completed_by: current_user.id)
129
122
 
130
123
  # Make sure bucket's div gets hidden if it's the last completed task in the bucket.
131
- if Task.bucket_empty?(params[:bucket], current_user)
132
- @empty_bucket = params[:bucket]
133
- end
124
+ @empty_bucket = params[:bucket] if Task.bucket_empty?(params[:bucket], current_user)
134
125
 
135
126
  update_sidebar unless params[:bucket].blank?
136
127
  respond_with(@task)
@@ -140,12 +131,10 @@ class TasksController < ApplicationController
140
131
  #----------------------------------------------------------------------------
141
132
  def uncomplete
142
133
  @task = Task.tracked_by(current_user).find(params[:id])
143
- @task&.update_attributes(completed_at: nil, completed_by: nil)
134
+ @task&.update(completed_at: nil, completed_by: nil)
144
135
 
145
136
  # Make sure bucket's div gets hidden if we're deleting last task in the bucket.
146
- if Task.bucket_empty?(params[:bucket], current_user, @view)
147
- @empty_bucket = params[:bucket]
148
- end
137
+ @empty_bucket = params[:bucket] if Task.bucket_empty?(params[:bucket], current_user, @view)
149
138
 
150
139
  update_sidebar
151
140
  respond_with(@task)
@@ -173,6 +162,7 @@ class TasksController < ApplicationController
173
162
 
174
163
  def task_params
175
164
  return {} unless params[:task]
165
+
176
166
  params.require(:task).permit(
177
167
  :user_id,
178
168
  :assigned_to,
@@ -186,7 +176,8 @@ class TasksController < ApplicationController
186
176
  :due_at,
187
177
  :completed_at,
188
178
  :deleted_at,
189
- :background_info
179
+ :background_info,
180
+ :calendar
190
181
  )
191
182
  end
192
183
 
@@ -9,6 +9,7 @@ class UsersController < ApplicationController
9
9
  before_action :set_current_tab, only: %i[show opportunities_overview] # Don't hightlight any tabs.
10
10
 
11
11
  check_authorization
12
+
12
13
  load_and_authorize_resource # handles all security
13
14
 
14
15
  respond_to :html, only: %i[show new]
@@ -21,30 +22,6 @@ class UsersController < ApplicationController
21
22
  respond_with(@user)
22
23
  end
23
24
 
24
- # GET /users/new
25
- # GET /users/new.js
26
- #----------------------------------------------------------------------------
27
- def new
28
- respond_with(@user)
29
- end
30
-
31
- # POST /users
32
- # POST /users.js
33
- #----------------------------------------------------------------------------
34
- def create
35
- if @user.save
36
- if Setting.user_signup == :needs_approval
37
- flash[:notice] = t(:msg_account_created)
38
- redirect_to login_url
39
- else
40
- flash[:notice] = t(:msg_successful_signup)
41
- redirect_back_or_default profile_url
42
- end
43
- else
44
- render :new
45
- end
46
- end
47
-
48
25
  # GET /users/1/edit.js
49
26
  #----------------------------------------------------------------------------
50
27
  def edit
@@ -55,7 +32,7 @@ class UsersController < ApplicationController
55
32
  # PUT /users/1.js
56
33
  #----------------------------------------------------------------------------
57
34
  def update
58
- @user.update_attributes(user_params)
35
+ @user.update(user_params)
59
36
  flash[:notice] = t(:msg_user_updated)
60
37
  respond_with(@user)
61
38
  end
@@ -77,9 +54,9 @@ class UsersController < ApplicationController
77
54
  render
78
55
  else
79
56
  if params[:avatar]
80
- avatar = Avatar.create(avatar_params)
81
- if avatar.valid?
82
- @user.avatar = avatar
57
+ @avatar = Avatar.create(avatar_params)
58
+ if @avatar.valid?
59
+ @user.avatar = @avatar
83
60
  else
84
61
  @user.avatar.errors.clear
85
62
  @user.avatar.errors.add(:image, t(:msg_bad_image_file))
@@ -104,7 +81,7 @@ class UsersController < ApplicationController
104
81
  # PUT /users/1/change_password.js
105
82
  #----------------------------------------------------------------------------
106
83
  def change_password
107
- if @user.valid_password?(params[:current_password], true) || @user.password_hash.blank?
84
+ if @user.valid_password?(params[:current_password])
108
85
  if params[:user][:password].blank?
109
86
  flash[:notice] = t(:msg_password_not_changed)
110
87
  else
@@ -138,7 +115,10 @@ class UsersController < ApplicationController
138
115
 
139
116
  def user_params
140
117
  return {} unless params[:user]
118
+
141
119
  params[:user][:email].try(:strip!)
120
+ params[:user][:alt_email].try(:strip!)
121
+
142
122
  params[:user].permit(
143
123
  :username,
144
124
  :email,
@@ -158,8 +138,9 @@ class UsersController < ApplicationController
158
138
 
159
139
  def avatar_params
160
140
  return {} unless params[:avatar]
141
+
161
142
  params[:avatar]
162
143
  .permit(:image)
163
- .merge(entity: @user)
144
+ .merge(entity: @user, user_id: @user.id)
164
145
  end
165
146
  end
@@ -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
@@ -27,7 +27,7 @@ module Admin::UsersHelper
27
27
  summary = []
28
28
  title_and_company = user_summary_title_and_company(user)
29
29
  summary << title_and_company unless title_and_company.blank?
30
- summary << t('pluralize.login', user.login_count) if user.current_login_at && user.login_count > 0
30
+ summary << t('pluralize.login', user.sign_in_count) if user.last_sign_in_at && user.sign_in_count.positive?
31
31
  summary << user.email
32
32
  summary << "#{t :phone_small}: #{user.phone}" unless user.phone.blank?
33
33
  summary << "#{t :mobile_small}: #{user.mobile}" unless user.mobile.blank?
@@ -18,7 +18,7 @@ module ApplicationHelper
18
18
 
19
19
  #----------------------------------------------------------------------------
20
20
  def tabless_layout?
21
- %w[authentications passwords].include?(controller.controller_name) ||
21
+ %w[sessions passwords registrations confirmations].include?(controller.controller_name) ||
22
22
  ((controller.controller_name == "users") && %w[create new].include?(controller.action_name))
23
23
  end
24
24
 
@@ -27,6 +27,7 @@ module ApplicationHelper
27
27
  def show_flash(options = { sticky: false })
28
28
  %i[error warning info notice alert].each do |type|
29
29
  next unless flash[type]
30
+
30
31
  html = content_tag(:div, h(flash[type]), id: "flash")
31
32
  flash[type] = nil
32
33
  return html << content_tag(:script, "crm.flash('#{type}', #{options[:sticky]})".html_safe, type: "text/javascript")
@@ -34,20 +35,23 @@ module ApplicationHelper
34
35
  content_tag(:p, nil, id: "flash", style: "display:none;")
35
36
  end
36
37
 
38
+ def subtitle_link(id, text, hidden)
39
+ link_to("<small>#{hidden ? '&#9658;' : '&#9660;'}</small> #{sanitize text}".html_safe,
40
+ url_for(controller: :home, action: :toggle, id: id),
41
+ remote: true,
42
+ onclick: "crm.flip_subtitle(this)")
43
+ end
44
+
37
45
  #----------------------------------------------------------------------------
38
46
  def subtitle(id, hidden = true, text = id.to_s.split("_").last.capitalize)
39
- content_tag("div",
40
- link_to("<small>#{hidden ? '&#9658;' : '&#9660;'}</small> #{sanitize text}".html_safe,
41
- url_for(controller: :home, action: :toggle, id: id),
42
- remote: true,
43
- onclick: "crm.flip_subtitle(this)"), class: "subtitle")
47
+ content_tag("div", subtitle_link(id, text, hidden), class: "subtitle")
44
48
  end
45
49
 
46
50
  #----------------------------------------------------------------------------
47
51
  def section(related, assets)
48
52
  asset = assets.to_s.singularize
49
- create_id = "create_#{asset}"
50
- select_id = "select_#{asset}"
53
+ create_id = "create_#{asset}"
54
+ select_id = "select_#{asset}"
51
55
  create_url = controller.send(:"new_#{asset}_path")
52
56
 
53
57
  html = tag(:br)
@@ -76,7 +80,7 @@ module ApplicationHelper
76
80
  #----------------------------------------------------------------------------
77
81
  def rating_select(name, options = {})
78
82
  stars = Hash[(1..5).map { |star| [star, "&#9733;" * star] }].sort
79
- options_for_select = %(<option value="0"#{options[:selected].to_i == 0 ? ' selected="selected"' : ''}>#{t :select_none}</option>)
83
+ options_for_select = %(<option value="0"#{options[:selected].to_i.zero? ? ' selected="selected"' : ''}>#{t :select_none}</option>)
80
84
  options_for_select += stars.map { |star| %(<option value="#{star.first}"#{options[:selected] == star.first ? ' selected="selected"' : ''}>#{star.last}</option>) }.join
81
85
  select_tag name, options_for_select.html_safe, options
82
86
  end
@@ -84,7 +88,6 @@ module ApplicationHelper
84
88
  #----------------------------------------------------------------------------
85
89
  def link_to_inline(id, url, options = {})
86
90
  text = options[:text] || t(id, default: id.to_s.titleize)
87
- text = (arrow_for(id) + text) unless options[:plain]
88
91
  related = (options[:related] ? "&related=#{options[:related]}" : '')
89
92
 
90
93
  link_to(text,
@@ -112,7 +115,6 @@ module ApplicationHelper
112
115
 
113
116
  #----------------------------------------------------------------------------
114
117
  def link_to_delete(record, options = {})
115
- object = record.is_a?(Array) ? record.last : record
116
118
  confirm = options[:confirm] || nil
117
119
 
118
120
  link_to(t(:delete) + "!",
@@ -231,16 +233,15 @@ module ApplicationHelper
231
233
 
232
234
  # Reresh sidebar using the action view within the current controller.
233
235
  #----------------------------------------------------------------------------
234
- def refresh_sidebar(action = nil, shake = nil)
235
- refresh_sidebar_for(controller.controller_name, action, shake)
236
+ def refresh_sidebar(action = nil)
237
+ refresh_sidebar_for(controller.controller_name, action)
236
238
  end
237
239
 
238
240
  # Refresh sidebar using the action view within an arbitrary controller.
239
241
  #----------------------------------------------------------------------------
240
- def refresh_sidebar_for(view, action = nil, shake = nil)
242
+ def refresh_sidebar_for(view, action = nil)
241
243
  text = ""
242
244
  text += "$('#sidebar').html('#{j render(partial: 'layouts/sidebar', locals: { view: view, action: action })}');"
243
- text += "$('##{j shake.to_s}').effect('shake', { duration:200, distance: 3 });" if shake
244
245
  text.html_safe
245
246
  end
246
247
 
@@ -254,7 +255,7 @@ module ApplicationHelper
254
255
  if site == :skype
255
256
  url = "callto:" + url
256
257
  else
257
- url = "http://" + url unless url.match?(/^https?:\/\//)
258
+ url = "http://" + url unless url.match?(%r{^https?://})
258
259
  end
259
260
  link_to(image_tag("#{site}.gif", size: "15x15"), h(url), "data-popup": true, title: t(:open_in_window, h(url)))
260
261
  end.compact.join("\n").html_safe
@@ -296,14 +297,12 @@ module ApplicationHelper
296
297
  # Ajax helper to pass browser timezone offset to the server.
297
298
  #----------------------------------------------------------------------------
298
299
  def get_browser_timezone_offset
299
- unless session[:timezone_offset]
300
- raw "$.get('#{timezone_path}', {offset: (new Date()).getTimezoneOffset()});"
301
- end
300
+ raw "$.get('#{timezone_path}', {offset: (new Date()).getTimezoneOffset()});" unless session[:timezone_offset]
302
301
  end
303
302
 
304
303
  # Entities can have associated avatars or gravatars. Only calls Gravatar
305
304
  # in production env. Gravatar won't serve default images if they are not
306
- # publically available: http://en.gravatar.com/site/implement/images
305
+ # publically available: https://en.gravatar.com/site/implement/images
307
306
  #----------------------------------------------------------------------------
308
307
  def avatar_for(model, args = {})
309
308
  args = { class: 'gravatar', size: :large }.merge(args)
@@ -327,17 +326,12 @@ module ApplicationHelper
327
326
 
328
327
  # Render a text field that is part of compound address.
329
328
  #----------------------------------------------------------------------------
330
- def address_field(form, object, attribute, extra_styles)
329
+ def address_field(form, attribute, extra_styles)
331
330
  hint = "#{t(attribute)}..."
332
- if object.send(attribute).blank?
333
- form.text_field(attribute,
334
- style: "margin-top: 6px; #{extra_styles}",
335
- placeholder: hint)
336
- else
337
- form.text_field(attribute,
338
- style: "margin-top: 6px; #{extra_styles}",
339
- placeholder: hint)
340
- end
331
+
332
+ form.text_field(attribute,
333
+ style: "margin-top: 6px; #{extra_styles}",
334
+ placeholder: hint)
341
335
  end
342
336
 
343
337
  # Return true if:
@@ -353,7 +347,7 @@ module ApplicationHelper
353
347
  # Helper to display links to supported data export formats.
354
348
  #----------------------------------------------------------------------------
355
349
  def links_to_export(action = :index)
356
- token = current_user.single_access_token
350
+ token = current_user.authentication_token
357
351
  url_params = { action: action }
358
352
  url_params[:id] = params[:id] unless params[:id].blank?
359
353
  url_params[:query] = params[:query] unless params[:query].blank?
@@ -389,7 +383,7 @@ module ApplicationHelper
389
383
  end
390
384
 
391
385
  def entity_filter_checkbox(name, value, count)
392
- checked = (session["#{controller_name}_filter"].present? ? session["#{controller_name}_filter"].split(",").include?(value.to_s) : count.to_i > 0)
386
+ checked = (session["#{controller_name}_filter"].present? ? session["#{controller_name}_filter"].split(",").include?(value.to_s) : count.to_i.positive?)
393
387
  url = url_for(action: :filter)
394
388
  onclick = %{
395
389
  var query = $('#query').val(),
@@ -421,7 +415,7 @@ module ApplicationHelper
421
415
  fmt_value = if email
422
416
  link_to_email(fmt_value)
423
417
  else
424
- fmt_value.gsub(/((http|ftp|https):\/\/[\w\-_]+(\.[\w\-_]+)+([\w\-\.,@?^=%&amp;:\/\+#]*[\w\-\@?^=%&amp;\/\+#])?)/, "<a href=\"\\1\">\\1</a>")
418
+ fmt_value.gsub(%r{((http|ftp|https)://[\w\-_]+(\.[\w\-_]+)+([\w\-\.,@?^=%&amp;:/\+#]*[\w\-\@?^=%&amp;/\+#])?)}, "<a href=\"\\1\">\\1</a>")
425
419
  end
426
420
  out << content_tag(:td, fmt_value, class: last_class)
427
421
  end
@@ -433,10 +427,7 @@ module ApplicationHelper
433
427
  def section_title(id, hidden = true, text = nil, info_text = nil)
434
428
  text = id.to_s.split("_").last.capitalize if text.nil?
435
429
  content_tag("div", class: "subtitle show_attributes") do
436
- content = link_to("<small>#{hidden ? '&#9658;' : '&#9660;'}</small> #{sanitize text}".html_safe,
437
- url_for(controller: :home, action: :toggle, id: id),
438
- remote: true,
439
- onclick: "crm.flip_subtitle(this)")
430
+ content = subtitle_link(id, text, hidden)
440
431
  content << content_tag("small", info_text.to_s, class: "subtitle_inline_info", id: "#{id}_intro", style: hidden ? "" : "display:none;")
441
432
  end
442
433
  end
@@ -455,6 +446,7 @@ module ApplicationHelper
455
446
  views = FatFreeCRM::ViewFactory.views_for(controller: controller.controller_name,
456
447
  action: show_or_index_action)
457
448
  return nil unless views.size > 1
449
+
458
450
  lis = ''.html_safe
459
451
  content_tag :ul, class: 'format-buttons' do
460
452
  views.collect do |view|
@@ -480,6 +472,7 @@ module ApplicationHelper
480
472
  # <span class="timeago" datetime="2008-07-17T09:24:17Z">July 17, 2008</span>
481
473
  def timeago(time, options = {})
482
474
  return unless time
475
+
483
476
  options[:class] ||= "timeago"
484
477
  options[:title] = time.getutc.iso8601
485
478
  content_tag(:span, I18n.l(time), options)
@@ -14,7 +14,7 @@ module CampaignsHelper
14
14
 
15
15
  #----------------------------------------------------------------------------
16
16
  def performance(actual, target)
17
- if target.to_i > 0 && actual.to_i > 0
17
+ if target.to_i.positive? && actual.to_i.positive?
18
18
  if target > actual
19
19
  n = 100 - actual * 100 / target
20
20
  html = content_tag(:span, "(-#{number_to_percentage(n, precision: 1)})", class: "warn")
@@ -12,9 +12,7 @@ module ContactsHelper
12
12
  summary = ['']
13
13
  summary << contact.title.titleize if contact.title?
14
14
  summary << contact.department if contact.department?
15
- if contact.account&.name?
16
- summary.last += " #{t(:at)} #{contact.account.name}"
17
- end
15
+ summary.last += " #{t(:at)} #{contact.account.name}" if contact.account&.name?
18
16
  summary << contact.email if contact.email.present?
19
17
  summary << "#{t(:phone_small)}: #{contact.phone}" if contact.phone.present?
20
18
  summary << "#{t(:mobile_small)}: #{contact.mobile}" if contact.mobile.present?
@@ -51,24 +51,16 @@ module OpportunitiesHelper
51
51
  msg = []
52
52
  won_or_lost = %w[won lost].include?(opportunity.stage)
53
53
 
54
- if opportunity.weighted_amount != 0
55
- msg << content_tag(:b, number_to_currency(opportunity.weighted_amount, precision: 0))
56
- end
54
+ msg << content_tag(:b, number_to_currency(opportunity.weighted_amount, precision: 0)) if opportunity.weighted_amount != 0
57
55
 
58
56
  unless won_or_lost
59
57
  if detailed
60
- if opportunity.amount.to_f != 0
61
- msg << number_to_currency(opportunity.amount.to_f, precision: 0)
62
- end
58
+ msg << number_to_currency(opportunity.amount.to_f, precision: 0) if opportunity.amount.to_f != 0
63
59
 
64
- if opportunity.discount.to_f != 0
65
- msg << t(:discount) + ' ' + number_to_currency(opportunity.discount, precision: 0)
66
- end
60
+ msg << t(:discount) + ' ' + number_to_currency(opportunity.discount, precision: 0) if opportunity.discount.to_f != 0
67
61
  end
68
62
 
69
- if opportunity.probability.to_i != 0
70
- msg << t(:probability) + ' ' + opportunity.probability.to_s + '%'
71
- end
63
+ msg << t(:probability) + ' ' + opportunity.probability.to_s + '%' if opportunity.probability.to_i != 0
72
64
  end
73
65
 
74
66
  msg << opportunity_closes_on_message(opportunity, won_or_lost)
@@ -11,7 +11,7 @@ module TasksHelper
11
11
  #----------------------------------------------------------------------------
12
12
  def task_filter_checkbox(view, filter, count)
13
13
  name = "filter_by_task_#{view}"
14
- checked = (session[name] ? session[name].split(",").include?(filter.to_s) : count > 0)
14
+ checked = (session[name] ? session[name].split(",").include?(filter.to_s) : count.positive?)
15
15
  url = url_for(action: :filter, view: view)
16
16
  onclick = %{
17
17
  $('#loading').show();
@@ -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
@@ -7,9 +7,7 @@
7
7
  #------------------------------------------------------------------------------
8
8
  module UsersHelper
9
9
  def language_for(user)
10
- if user.preference[:locale]
11
- _locale, language = languages.detect { |locale, _language| locale == user.preference[:locale] }
12
- end
10
+ _locale, language = languages.detect { |locale, _language| locale == user.preference[:locale] } if user.preference[:locale]
13
11
  language || "English"
14
12
  end
15
13
 
@@ -5,4 +5,8 @@
5
5
  # Fat Free CRM is freely distributable under the terms of MIT license.
6
6
  # See MIT-LICENSE file or http://www.opensource.org/licenses/mit-license.php
7
7
  #------------------------------------------------------------------------------
8
- PaperTrail.config.track_associations = false
8
+ class DeviseMailer < Devise::Mailer
9
+ def template_paths
10
+ ["devise/mailer"]
11
+ end
12
+ end
@@ -6,15 +6,6 @@
6
6
  # See MIT-LICENSE file or http://www.opensource.org/licenses/mit-license.php
7
7
  #------------------------------------------------------------------------------
8
8
  class UserMailer < ActionMailer::Base
9
- def password_reset_instructions(user)
10
- @edit_password_url = edit_password_url(user.perishable_token)
11
-
12
- mail subject: "Fat Free CRM: " + I18n.t(:password_reset_instruction),
13
- to: user.email,
14
- from: from_address,
15
- date: Time.now
16
- end
17
-
18
9
  def assigned_entity_notification(entity, assigner)
19
10
  @entity_url = url_for(entity)
20
11
  @entity_name = entity.name
@@ -28,8 +28,8 @@
28
28
  #
29
29
 
30
30
  class Account < ActiveRecord::Base
31
- belongs_to :user
32
- belongs_to :assignee, class_name: "User", foreign_key: :assigned_to
31
+ belongs_to :user, optional: true # TODO: Is this really optional?
32
+ belongs_to :assignee, class_name: "User", foreign_key: :assigned_to, optional: true
33
33
  has_many :account_contacts, dependent: :destroy
34
34
  has_many :contacts, -> { distinct }, through: :account_contacts
35
35
  has_many :account_opportunities, dependent: :destroy
@@ -46,7 +46,7 @@ class Account < ActiveRecord::Base
46
46
  accepts_nested_attributes_for :billing_address, allow_destroy: true, reject_if: proc { |attributes| Address.reject_address(attributes) }
47
47
  accepts_nested_attributes_for :shipping_address, allow_destroy: true, reject_if: proc { |attributes| Address.reject_address(attributes) }
48
48
 
49
- scope :state, ->(filters) {
49
+ scope :state, lambda { |filters|
50
50
  where('category IN (?)' + (filters.delete('other') ? ' OR category IS NULL' : ''), filters)
51
51
  }
52
52
  scope :created_by, ->(user) { where(user_id: user.id) }
@@ -54,7 +54,7 @@ class Account < ActiveRecord::Base
54
54
 
55
55
  scope :text_search, ->(query) { ransack('name_or_email_cont' => query).result }
56
56
 
57
- scope :visible_on_dashboard, ->(user) {
57
+ scope :visible_on_dashboard, lambda { |user|
58
58
  # Show accounts which either belong to the user and are unassigned, or are assigned to the user
59
59
  where('(user_id = :user_id AND assigned_to IS NULL) OR assigned_to = :user_id', user_id: user.id)
60
60
  }
@@ -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"
@@ -91,6 +91,7 @@ class Account < ActiveRecord::Base
91
91
  #----------------------------------------------------------------------------
92
92
  def location
93
93
  return "" unless self[:billing_address]
94
+
94
95
  location = self[:billing_address].strip.split("\n").last
95
96
  location&.gsub(/(^|\s+)\d+(:?\s+|$)/, " ")&.strip
96
97
  end
@@ -98,9 +99,7 @@ class Account < ActiveRecord::Base
98
99
  # Attach given attachment to the account if it hasn't been attached already.
99
100
  #----------------------------------------------------------------------------
100
101
  def attach!(attachment)
101
- unless send("#{attachment.class.name.downcase}_ids").include?(attachment.id)
102
- send(attachment.class.name.tableize) << attachment
103
- end
102
+ send(attachment.class.name.tableize) << attachment unless send("#{attachment.class.name.downcase}_ids").include?(attachment.id)
104
103
  end
105
104
 
106
105
  # Discard given attachment from the account.
@@ -117,14 +116,15 @@ class Account < ActiveRecord::Base
117
116
  #----------------------------------------------------------------------------
118
117
  def self.create_or_select_for(model, params)
119
118
  # Attempt to find existing account
120
- if params[:id].present?
121
- return Account.find(params[:id])
122
- elsif params[:name].present?
119
+ return Account.find(params[:id]) if params[:id].present?
120
+
121
+ if params[:name].present?
123
122
  account = Account.find_by(name: params[:name])
124
123
  return account if account
125
124
  end
126
125
 
127
126
  # Fallback to create new account
127
+ params[:user] = model.user if model
128
128
  account = Account.new(params)
129
129
  if account.access != "Lead" || model.nil?
130
130
  account.save
@@ -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