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.
- checksums.yaml +4 -4
- data/.docker/nginx/sites-enabled/ffcrm.conf +8 -0
- data/.gitignore +2 -0
- data/.rubocop.yml +4 -1
- data/.rubocop_todo.yml +190 -89
- data/.travis.yml +10 -3
- data/CHANGELOG.md +27 -0
- data/{CONTRIBUTORS → CONTRIBUTORS.md} +2 -1
- data/Dockerfile +15 -13
- data/Gemfile +7 -4
- data/Gemfile.lock +200 -167
- data/README.md +4 -2
- data/app/assets/javascripts/crm_sortable.js.coffee +5 -0
- data/app/assets/javascripts/timeago.js.coffee +5 -0
- data/app/assets/stylesheets/about.css.scss +5 -0
- data/app/assets/stylesheets/common.scss +2 -1
- data/app/controllers/admin/fields_controller.rb +1 -1
- data/app/controllers/admin/groups_controller.rb +5 -1
- data/app/controllers/admin/tags_controller.rb +1 -1
- data/app/controllers/admin/users_controller.rb +10 -6
- data/app/controllers/application_controller.rb +13 -13
- data/app/controllers/authentications_controller.rb +2 -2
- data/app/controllers/comments_controller.rb +3 -2
- data/app/controllers/entities/contacts_controller.rb +9 -7
- data/app/controllers/entities/opportunities_controller.rb +1 -1
- data/app/controllers/entities_controller.rb +4 -4
- data/app/controllers/home_controller.rb +9 -9
- data/app/controllers/passwords_controller.rb +1 -1
- data/app/controllers/tasks_controller.rb +2 -1
- data/app/controllers/users_controller.rb +4 -2
- data/app/helpers/application_helper.rb +9 -9
- data/app/helpers/javascript_helper.rb +5 -0
- data/app/helpers/opportunities_helper.rb +1 -1
- data/app/helpers/remote_link_pagination_helper.rb +5 -0
- data/app/inputs/date_pair_input.rb +1 -1
- data/app/models/entities/account.rb +2 -2
- data/app/models/entities/account_contact.rb +1 -1
- data/app/models/entities/campaign.rb +3 -3
- data/app/models/entities/contact.rb +3 -3
- data/app/models/entities/lead.rb +2 -2
- data/app/models/entities/opportunity.rb +3 -3
- data/app/models/fields/custom_field.rb +1 -1
- data/app/models/fields/custom_field_pair.rb +2 -2
- data/app/models/fields/field.rb +1 -1
- data/app/models/polymorphic/address.rb +2 -2
- data/app/models/polymorphic/avatar.rb +4 -2
- data/app/models/polymorphic/email.rb +8 -6
- data/app/models/polymorphic/task.rb +3 -1
- data/app/models/polymorphic/version.rb +3 -3
- data/app/models/users/ability.rb +1 -1
- data/app/models/users/permission.rb +2 -0
- data/app/models/users/user.rb +2 -2
- data/app/views/accounts/index.js.haml +1 -1
- data/app/views/admin/fields/_sort_by.html.haml +1 -1
- data/app/views/admin/users/_user.html.haml +1 -1
- data/app/views/campaigns/index.js.haml +1 -1
- data/app/views/contacts/index.js.haml +1 -1
- data/app/views/entities/_basic_search.html.haml +1 -1
- data/app/views/home/_duration_menu.html.haml +1 -1
- data/app/views/home/_events_menu.html.haml +1 -1
- data/app/views/home/_users_menu.html.haml +1 -1
- data/app/views/layouts/application.html.haml +1 -1
- data/app/views/leads/index.js.haml +1 -1
- data/app/views/opportunities/index.js.haml +1 -1
- data/app/views/shared/_naming.html.haml +1 -1
- data/app/views/users/_languages.html.haml +1 -1
- data/config/application.rb +2 -3
- data/config/boot.rb +2 -0
- data/config/deploy.example.rb +1 -1
- data/config/environments/development.rb +2 -0
- data/config/environments/production.rb +1 -1
- data/config/environments/test.rb +2 -2
- data/config/initializers/assets.rb +6 -1
- data/config/initializers/backtrace_silencers.rb +5 -0
- data/config/initializers/constants.rb +1 -1
- data/config/initializers/cookies_serializer.rb +5 -0
- data/config/initializers/custom_field_ransack_translations.rb +5 -0
- data/config/initializers/filter_parameter_logging.rb +5 -0
- data/config/initializers/inflections.rb +5 -0
- data/config/initializers/paper_trail.rb +5 -0
- data/config/initializers/ransack.rb +3 -3
- data/config/initializers/session_store.rb +5 -0
- data/config/initializers/wrap_parameters.rb +5 -0
- data/config/locales/et.yml +207 -0
- data/config/locales/et_fat_free_crm.yml +928 -0
- data/config/locales/pt-BR_ransack.yml +81 -0
- data/config/locales/th.rb +1 -1
- data/config/routes.rb +18 -18
- data/db/migrate/20100928030598_create_sessions.rb +1 -1
- data/db/migrate/20100928030599_create_users.rb +2 -2
- data/db/migrate/20100928030600_create_openid_tables.rb +1 -1
- data/db/migrate/20100928030601_create_accounts.rb +2 -2
- data/db/migrate/20100928030602_create_permissions.rb +1 -1
- data/db/migrate/20100928030603_create_settings.rb +1 -1
- data/db/migrate/20100928030604_create_preferences.rb +2 -2
- data/db/migrate/20100928030605_create_campaigns.rb +2 -2
- data/db/migrate/20100928030606_create_leads.rb +2 -2
- data/db/migrate/20100928030607_create_contacts.rb +2 -2
- data/db/migrate/20100928030608_create_opportunities.rb +2 -2
- data/db/migrate/20100928030609_create_account_contacts.rb +1 -1
- data/db/migrate/20100928030610_create_account_opportunities.rb +1 -1
- data/db/migrate/20100928030611_create_contact_opportunities.rb +1 -1
- data/db/migrate/20100928030612_create_tasks.rb +2 -2
- data/db/migrate/20100928030613_create_comments.rb +1 -1
- data/db/migrate/20100928030614_create_activities.rb +1 -1
- data/db/migrate/20100928030615_create_avatars.rb +1 -1
- data/db/migrate/20100928030616_rename_remember_token.rb +1 -1
- data/db/migrate/20100928030617_drop_openid_tables.rb +1 -1
- data/db/migrate/20100928030618_add_admin_to_users.rb +1 -1
- data/db/migrate/20100928030619_add_suspended_to_users.rb +1 -1
- data/db/migrate/20100928030620_remove_uuid.rb +2 -2
- data/db/migrate/20100928030621_add_email_to_accounts.rb +1 -1
- data/db/migrate/20100928030622_add_background_info_to_models.rb +1 -1
- data/db/migrate/20100928030623_create_addresses.rb +2 -2
- data/db/migrate/20100928030624_add_index_on_permissions.rb +3 -3
- data/db/migrate/20100928030625_create_emails.rb +2 -2
- data/db/migrate/20100928030626_add_state_to_timeline_objects.rb +1 -1
- data/db/migrate/20100928030627_acts_as_taggable_on_migration.rb +2 -2
- data/db/migrate/20101221123456_add_single_access_token_to_users.rb +1 -1
- data/db/migrate/20101221345678_add_rating_and_category_to_accounts.rb +1 -1
- data/db/migrate/20110719082054_add_skype_to_contacts_and_leads.rb +1 -1
- data/db/migrate/20111101083437_create_fields.rb +1 -1
- data/db/migrate/20111101090312_create_field_groups.rb +1 -1
- data/db/migrate/20111116091952_add_field_groups_tag_id.rb +1 -1
- data/db/migrate/20111117041311_change_fields_collection_to_text.rb +1 -1
- data/db/migrate/20111201030535_add_field_groups_klass_name.rb +2 -2
- data/db/migrate/20120121054235_create_lists.rb +1 -1
- data/db/migrate/20120216031616_create_versions.rb +3 -3
- data/db/migrate/20120216042541_is_paranoid_to_paper_trail.rb +1 -1
- data/db/migrate/20120220233724_add_versions_object_changes.rb +1 -1
- data/db/migrate/20120224073107_remove_default_value_and_clear_settings.rb +1 -1
- data/db/migrate/20120309070209_add_versions_related.rb +1 -1
- data/db/migrate/20120314080441_add_subscribed_users_to_entities.rb +2 -2
- data/db/migrate/20120316045804_activities_to_versions.rb +1 -1
- data/db/migrate/20120405080727_change_subscribed_users_to_set.rb +1 -1
- data/db/migrate/20120405080742_change_further_subscribed_users_to_set.rb +2 -2
- data/db/migrate/20120406082136_create_groups.rb +2 -2
- data/db/migrate/20120413034923_add_index_on_versions_item_type.rb +1 -1
- data/db/migrate/20120510025219_add_not_null_constraints_for_timestamp_columns.rb +3 -3
- data/db/migrate/20120528102124_increase_length_of_version_events.rb +1 -1
- data/db/migrate/20120801032706_add_pair_id_to_fields.rb +1 -1
- data/db/migrate/20121003063155_add_settings_to_custom_fields.rb +1 -1
- data/db/migrate/20121221033947_fix_country_mapping.rb +1 -1
- data/db/migrate/20131207033244_add_user_id_to_lists.rb +1 -1
- data/db/migrate/20140916011927_add_created_at_index_on_versions.rb +1 -1
- data/db/migrate/20140916012922_add_indexes_to_model_associations.rb +3 -3
- data/db/migrate/20141126031837_increase_email_to254_chars.rb +1 -1
- data/db/migrate/20141230021159_add_transaction_id_column_to_versions.rb +1 -1
- data/db/migrate/20141230205453_add_missing_unique_indices.acts_as_taggable_on_engine.rb +4 -4
- data/db/migrate/20141230205454_add_taggings_counter_cache_to_tags.acts_as_taggable_on_engine.rb +1 -1
- data/db/migrate/20141230205455_add_missing_taggable_index.acts_as_taggable_on_engine.rb +3 -3
- data/db/migrate/20150123060900_convert_radio_to_radio_buttons.rb +1 -1
- data/db/migrate/20150227123054_remove_last_request_at_from_users.rb +1 -1
- data/db/migrate/20150427131956_create_index_related_type.rb +3 -3
- data/db/migrate/20160511053730_add_account_contacts_index.rb +2 -2
- data/docker-compose.yml +17 -13
- data/fat_free_crm.gemspec +4 -6
- data/lib/development_tasks/license.rake +12 -9
- data/lib/fat_free_crm/callback.rb +4 -4
- data/lib/fat_free_crm/engine.rb +2 -2
- data/lib/fat_free_crm/exportable.rb +2 -2
- data/lib/fat_free_crm/fields.rb +2 -4
- data/lib/fat_free_crm/gem_ext.rb +0 -1
- data/lib/fat_free_crm/gem_ext/rake/task.rb +2 -0
- data/lib/fat_free_crm/mail_processor/base.rb +4 -4
- data/lib/fat_free_crm/mail_processor/dropbox.rb +1 -1
- data/lib/fat_free_crm/permissions.rb +5 -5
- data/lib/fat_free_crm/sortable.rb +1 -1
- data/lib/fat_free_crm/version.rb +1 -1
- data/lib/gravatar_image_tag.rb +1 -0
- data/lib/tasks/ffcrm/demo.rake +2 -2
- data/spec/controllers/admin/users_controller_spec.rb +25 -25
- data/spec/controllers/authentications_controller_spec.rb +9 -9
- data/spec/controllers/comments_controller_spec.rb +15 -15
- data/spec/controllers/emails_controller_spec.rb +2 -2
- data/spec/controllers/entities/accounts_controller_spec.rb +46 -46
- data/spec/controllers/entities/campaigns_controller_spec.rb +46 -46
- data/spec/controllers/entities/contacts_controller_spec.rb +55 -55
- data/spec/controllers/entities/leads_controller_spec.rb +85 -85
- data/spec/controllers/entities/opportunities_controller_spec.rb +74 -74
- data/spec/controllers/home_controller_spec.rb +11 -11
- data/spec/controllers/passwords_controller_spec.rb +2 -2
- data/spec/controllers/tasks_controller_spec.rb +37 -37
- data/spec/controllers/users_controller_spec.rb +31 -31
- data/spec/factories/campaign_factories.rb +1 -1
- data/spec/factories/contact_factories.rb +1 -1
- data/spec/factories/field_factories.rb +1 -1
- data/spec/factories/lead_factories.rb +2 -2
- data/spec/factories/opportunity_factories.rb +3 -3
- data/spec/factories/shared_factories.rb +1 -1
- data/spec/factories/task_factories.rb +1 -1
- data/spec/features/support/browser.rb +9 -1
- data/spec/lib/fields_spec.rb +2 -2
- data/spec/lib/permissions_spec.rb +38 -6
- data/spec/lib/view_factory_spec.rb +2 -2
- data/spec/models/fields/custom_field_spec.rb +3 -3
- data/spec/models/observers/entity_observer_spec.rb +1 -1
- data/spec/models/polymorphic/version_spec.rb +11 -11
- data/spec/models/users/abilities/user_ability_spec.rb +8 -3
- data/spec/models/users/permission_spec.rb +8 -0
- data/spec/models/users/user_spec.rb +1 -1
- data/spec/shared/controllers.rb +10 -10
- data/spec/spec_helper.rb +1 -1
- data/spec/views/accounts/index.haml_spec.rb +1 -1
- data/spec/views/accounts/update.js.haml_spec.rb +2 -2
- data/spec/views/admin/users/update.js.haml_spec.rb +2 -2
- data/spec/views/application/auto_complete.haml_spec.rb +1 -1
- data/spec/views/campaigns/index.haml_spec.rb +1 -1
- data/spec/views/campaigns/update.js.haml_spec.rb +2 -2
- data/spec/views/contacts/index.haml_spec.rb +1 -1
- data/spec/views/contacts/update.js.haml_spec.rb +2 -2
- data/spec/views/home/options.js.haml_spec.rb +1 -1
- data/spec/views/leads/index.haml_spec.rb +1 -1
- data/spec/views/leads/promote.js.haml_spec.rb +2 -2
- data/spec/views/leads/update.js.haml_spec.rb +2 -2
- data/spec/views/opportunities/index.haml_spec.rb +1 -1
- data/spec/views/opportunities/update.js.haml_spec.rb +1 -1
- data/spec/views/tasks/_edit.haml_spec.rb +2 -2
- data/spec/views/tasks/create.js.haml_spec.rb +2 -2
- data/spec/views/tasks/edit.js.haml_spec.rb +1 -1
- data/spec/views/users/change_password.js.haml_spec.rb +2 -2
- data/spec/views/users/update.js.haml_spec.rb +2 -2
- data/spec/views/users/upload_avatar.js.haml_spec.rb +2 -2
- metadata +17 -20
- 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
|
20
|
-
((controller.controller_name == "users") && %w
|
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
|
-
[
|
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 = [
|
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
|
-
[
|
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
|
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
|
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 =
|
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
|
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
|
-
[
|
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) {
|
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
|
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: [
|
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) {
|
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
|
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: [
|
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
|
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
|
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)
|
data/app/models/entities/lead.rb
CHANGED
@@ -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) {
|
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
|
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
|
-
|
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
|
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 [
|
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
|
|
@@ -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
|
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
|
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
|
data/app/models/fields/field.rb
CHANGED
@@ -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
|
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
|
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
|
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:
|
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] = [
|
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
|
-
|
50
|
-
|
51
|
-
RedCloth
|
52
|
-
|
53
|
-
|
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
|
-
|
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
|
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
|
10
|
-
EVENTS = %w
|
11
|
-
DURATION = %w
|
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
|
data/app/models/users/ability.rb
CHANGED
@@ -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
|
data/app/models/users/user.rb
CHANGED
@@ -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
|
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
|
-
[
|
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(:
|
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(:
|
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(:
|
11
|
+
$('#search_results_count').html('#{ j render(:plain => t('search_results_count', :count => @search_results_count)) }');
|