fat_free_crm 0.11.1 → 0.11.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of fat_free_crm might be problematic. Click here for more details.
- data/Gemfile +30 -12
- data/Gemfile.lock +131 -119
- data/Procfile +1 -1
- data/README.md +1 -1
- data/app/assets/images/notifications.png +0 -0
- data/app/assets/javascripts/application.js.erb +3 -0
- data/app/assets/javascripts/crm_textarea_autocomplete.js +44 -0
- data/app/assets/stylesheets/application.css.erb +2 -0
- data/app/assets/stylesheets/common.scss +7 -11
- data/app/assets/stylesheets/textarea_autocomplete.scss +42 -0
- data/app/controllers/admin/application_controller.rb +5 -5
- data/app/controllers/admin/field_groups_controller.rb +11 -51
- data/app/controllers/admin/fields_controller.rb +13 -59
- data/app/controllers/admin/plugins_controller.rb +1 -4
- data/app/controllers/admin/settings_controller.rb +0 -4
- data/app/controllers/admin/tags_controller.rb +11 -66
- data/app/controllers/admin/users_controller.rb +20 -83
- data/app/controllers/application_controller.rb +83 -69
- data/app/controllers/comments_controller.rb +12 -29
- data/app/controllers/emails_controller.rb +1 -5
- data/app/controllers/entities/accounts_controller.rb +13 -32
- data/app/controllers/entities/campaigns_controller.rb +17 -32
- data/app/controllers/entities/contacts_controller.rb +20 -38
- data/app/controllers/entities/leads_controller.rb +33 -55
- data/app/controllers/entities/opportunities_controller.rb +26 -42
- data/app/controllers/entities_controller.rb +92 -83
- data/app/controllers/home_controller.rb +1 -10
- data/app/controllers/lists_controller.rb +1 -4
- data/app/controllers/{entities/tasks_controller.rb → tasks_controller.rb} +21 -32
- data/app/controllers/users_controller.rb +6 -5
- data/app/helpers/accounts_helper.rb +32 -9
- data/app/helpers/application_helper.rb +15 -1
- data/app/helpers/campaigns_helper.rb +1 -1
- data/app/helpers/comments_helper.rb +11 -1
- data/app/helpers/leads_helper.rb +1 -1
- data/app/helpers/opportunities_helper.rb +1 -1
- data/app/{models/mailers/notifier.rb → mailers/dropbox_mailer.rb} +5 -16
- data/app/mailers/subscription_mailer.rb +37 -0
- data/{lib/tasks/dropbox.rake → app/mailers/user_mailer.rb} +11 -13
- data/app/models/entities/account.rb +3 -1
- data/app/models/entities/campaign.rb +3 -1
- data/app/models/entities/contact.rb +3 -1
- data/app/models/entities/lead.rb +6 -5
- data/app/models/entities/opportunity.rb +3 -1
- data/app/models/fields/field.rb +1 -1
- data/app/models/polymorphic/comment.rb +34 -0
- data/app/models/{entities → polymorphic}/task.rb +16 -3
- data/app/models/setting.rb +15 -15
- data/app/models/users/ability.rb +12 -5
- data/app/models/users/user.rb +7 -2
- data/app/views/accounts/index.html.haml +1 -1
- data/app/views/accounts/index.js.rjs +1 -1
- data/app/views/admin/plugins/index.html.haml +1 -7
- data/app/views/{shared/auto_complete.html.haml → application/_auto_complete.html.haml} +0 -0
- data/app/views/{shared → application}/index.atom.builder +1 -1
- data/app/views/{shared → application}/index.rss.builder +1 -1
- data/app/views/campaigns/index.html.haml +1 -1
- data/app/views/campaigns/index.js.rjs +1 -1
- data/app/views/comments/_new.html.haml +6 -0
- data/app/views/comments/_subscription_links.html.haml +13 -0
- data/app/views/comments/new.js.rjs +2 -0
- data/app/views/contacts/_top_section.html.haml +3 -13
- data/app/views/contacts/index.html.haml +1 -1
- data/app/views/contacts/index.js.rjs +1 -1
- data/app/views/{notifier/dropbox_ack_notification.html.haml → dropbox_mailer/dropbox_notification.html.haml} +2 -2
- data/app/views/{shared → entities}/attach.js.rjs +1 -1
- data/app/views/entities/contacts.js.rjs +1 -1
- data/app/views/{shared/discard.rjs → entities/discard.js.rjs} +0 -0
- data/app/views/entities/leads.js.rjs +1 -1
- data/app/views/entities/opportunities.js.rjs +1 -1
- data/app/views/entities/subscription_update.js.rjs +4 -0
- data/app/views/entities/versions.js.rjs +1 -1
- data/app/views/layouts/_footer.html.haml +1 -1
- data/app/views/layouts/application.html.haml +3 -0
- data/app/views/leads/_contact.html.haml +1 -0
- data/app/views/leads/index.html.haml +1 -1
- data/app/views/leads/index.js.rjs +1 -1
- data/app/views/opportunities/_top_section.html.haml +4 -14
- data/app/views/opportunities/index.html.haml +1 -1
- data/app/views/opportunities/index.js.rjs +1 -1
- data/app/views/subscription_mailer/comment_notification.text.erb +7 -0
- data/app/views/{notifier → user_mailer}/password_reset_instructions.html.haml +0 -0
- data/config/application.rb +3 -1
- data/config/environments/development.rb +1 -1
- data/config/environments/test.rb +3 -0
- data/config/initializers/action_mailer.rb +8 -5
- data/config/initializers/cancan.rb +151 -0
- data/config/initializers/constants.rb +1 -0
- data/config/initializers/locale.rb +20 -0
- data/config/initializers/paper_trail.rb +4 -5
- data/config/initializers/relative_url_root.rb +0 -1
- data/config/initializers/squeel.rb +5 -0
- data/config/locales/cz_fat_free_crm.yml +3 -3
- data/config/locales/de.yml +2 -2
- data/config/locales/de_fat_free_crm.yml +651 -596
- data/config/locales/en-GB_fat_free_crm.yml +3 -3
- data/config/locales/en-US_fat_free_crm.yml +13 -3
- data/config/locales/es_fat_free_crm.yml +3 -3
- data/config/locales/fr-CA_fat_free_crm.yml +3 -3
- data/config/locales/fr_fat_free_crm.yml +3 -3
- data/config/locales/it_fat_free_crm.yml +3 -3
- data/config/locales/pl_fat_free_crm.yml +3 -3
- data/config/locales/pt-BR_fat_free_crm.yml +3 -3
- data/config/locales/ru_fat_free_crm.yml +3 -3
- data/config/locales/sv-SE_fat_free_crm.yml +3 -3
- data/config/locales/th_fat_free_crm.yml +3 -3
- data/config/routes.rb +10 -0
- data/config/settings.default.yml +29 -10
- data/config/unicorn.rb +4 -0
- data/db/migrate/20111201030535_add_field_groups_klass_name.rb +3 -1
- data/db/migrate/20120314080441_add_subscribed_users_to_entities.rb +23 -0
- data/db/migrate/20120405080727_change_subscribed_users_to_set.rb +24 -0
- data/db/migrate/20120405080742_change_further_subscribed_users_to_set.rb +27 -0
- data/db/migrate/20120413034923_add_index_on_versions_item_type.rb +5 -0
- data/db/schema.rb +109 -126
- data/fat_free_crm.gemspec +12 -18
- data/lib/fat_free_crm.rb +0 -1
- data/lib/fat_free_crm/core_ext/array.rb +1 -0
- data/lib/fat_free_crm/gem_dependencies.rb +1 -0
- data/lib/fat_free_crm/mail_processor/base.rb +226 -0
- data/lib/fat_free_crm/mail_processor/comment_replies.rb +86 -0
- data/lib/fat_free_crm/mail_processor/dropbox.rb +288 -0
- data/lib/fat_free_crm/permissions.rb +6 -19
- data/lib/fat_free_crm/renderers.rb +0 -8
- data/lib/fat_free_crm/tabs.rb +1 -1
- data/lib/fat_free_crm/version.rb +1 -1
- data/lib/plugins/country_select/lib/country_select.rb +2 -2
- data/lib/tasks/mail_processing.rake +60 -0
- data/spec/controllers/admin/users_controller_spec.rb +0 -2
- data/spec/controllers/{accounts_controller_spec.rb → entities/accounts_controller_spec.rb} +7 -9
- data/spec/controllers/{campaigns_controller_spec.rb → entities/campaigns_controller_spec.rb} +7 -7
- data/spec/controllers/{contacts_controller_spec.rb → entities/contacts_controller_spec.rb} +5 -9
- data/spec/controllers/{leads_controller_spec.rb → entities/leads_controller_spec.rb} +7 -9
- data/spec/controllers/{opportunities_controller_spec.rb → entities/opportunities_controller_spec.rb} +8 -15
- data/spec/controllers/tasks_controller_spec.rb +1 -5
- data/spec/controllers/users_controller_spec.rb +5 -9
- data/spec/factories/subscription_factories.rb +6 -0
- data/spec/lib/mail_processor/base_spec.rb +164 -0
- data/spec/lib/mail_processor/comment_replies_spec.rb +63 -0
- data/spec/lib/{dropbox_spec.rb → mail_processor/dropbox_spec.rb} +73 -181
- data/spec/lib/mail_processor/sample_emails/dropbox.rb +167 -0
- data/spec/mailers/subscription_mailer_spec.rb +17 -0
- data/spec/models/{base → entities}/account_contact_spec.rb +0 -0
- data/spec/models/{base → entities}/account_opportunity_spec.rb +0 -0
- data/spec/models/{base → entities}/account_spec.rb +4 -0
- data/spec/models/{base → entities}/campaign_spec.rb +4 -0
- data/spec/models/{base → entities}/contact_opportunity_spec.rb +0 -0
- data/spec/models/{base → entities}/contact_spec.rb +4 -0
- data/spec/models/{base → entities}/lead_spec.rb +4 -0
- data/spec/models/{base → entities}/opportunity_spec.rb +4 -0
- data/spec/models/polymorphic/comment_spec.rb +15 -0
- data/spec/models/{base → polymorphic}/task_spec.rb +124 -30
- data/spec/models/polymorphic/version_spec.rb +1 -1
- data/spec/shared/controllers.rb +5 -7
- data/spec/shared/models.rb +46 -0
- data/spec/spec_helper.rb +3 -4
- data/spec/support/mail_processor_mocks.rb +30 -0
- data/spec/support/uploaded_file.rb +3 -0
- data/spec/views/{common → application}/auto_complete.haml_spec.rb +1 -1
- data/vendor/assets/images/jquery-ui/ui-bg_diagonals-thick_18_b81900_40x40.png +0 -0
- data/vendor/assets/images/jquery-ui/ui-bg_diagonals-thick_20_666666_40x40.png +0 -0
- data/vendor/assets/images/jquery-ui/ui-bg_flat_10_000000_40x100.png +0 -0
- data/vendor/assets/images/jquery-ui/ui-bg_glass_100_f6f6f6_1x400.png +0 -0
- data/vendor/assets/images/jquery-ui/ui-bg_glass_100_fdf5ce_1x400.png +0 -0
- data/vendor/assets/images/jquery-ui/ui-bg_glass_65_ffffff_1x400.png +0 -0
- data/vendor/assets/images/jquery-ui/ui-bg_gloss-wave_35_f6a828_500x100.png +0 -0
- data/vendor/assets/images/jquery-ui/ui-bg_highlight-soft_100_eeeeee_1x100.png +0 -0
- data/vendor/assets/images/jquery-ui/ui-bg_highlight-soft_75_ffe45c_1x100.png +0 -0
- data/vendor/assets/images/jquery-ui/ui-icons_222222_256x240.png +0 -0
- data/vendor/assets/images/jquery-ui/ui-icons_228ef1_256x240.png +0 -0
- data/vendor/assets/images/jquery-ui/ui-icons_ef8c08_256x240.png +0 -0
- data/vendor/assets/images/jquery-ui/ui-icons_ffd27a_256x240.png +0 -0
- data/vendor/assets/images/jquery-ui/ui-icons_ffffff_256x240.png +0 -0
- data/vendor/assets/javascripts/textarea_autocomplete.js +605 -0
- data/vendor/assets/stylesheets/jquery-ui.custom.css.erb +565 -0
- metadata +234 -154
- data/config/locales/simple_form.en.yml +0 -24
- data/lib/fat_free_crm/dropbox.rb +0 -439
- data/spec/lib/dropbox/email_samples.rb +0 -77
@@ -49,6 +49,8 @@ class Account < ActiveRecord::Base
|
|
49
49
|
has_one :shipping_address, :dependent => :destroy, :as => :addressable, :class_name => "Address", :conditions => "address_type = 'Shipping'"
|
50
50
|
has_many :emails, :as => :mediator
|
51
51
|
|
52
|
+
serialize :subscribed_users, Set
|
53
|
+
|
52
54
|
accepts_nested_attributes_for :billing_address, :allow_destroy => true
|
53
55
|
accepts_nested_attributes_for :shipping_address, :allow_destroy => true
|
54
56
|
|
@@ -66,7 +68,7 @@ class Account < ActiveRecord::Base
|
|
66
68
|
uses_user_permissions
|
67
69
|
acts_as_commentable
|
68
70
|
acts_as_taggable_on :tags
|
69
|
-
has_paper_trail
|
71
|
+
has_paper_trail :ignore => [ :subscribed_users ]
|
70
72
|
has_fields
|
71
73
|
exportable
|
72
74
|
sortable :by => [ "name ASC", "rating DESC", "created_at DESC", "updated_at DESC" ], :default => "created_at DESC"
|
@@ -49,6 +49,8 @@ class Campaign < ActiveRecord::Base
|
|
49
49
|
has_many :opportunities, :dependent => :destroy, :order => "id DESC"
|
50
50
|
has_many :emails, :as => :mediator
|
51
51
|
|
52
|
+
serialize :subscribed_users, Set
|
53
|
+
|
52
54
|
scope :state, lambda { |filters|
|
53
55
|
where('status IN (?)' + (filters.delete('other') ? ' OR status IS NULL' : ''), filters)
|
54
56
|
}
|
@@ -63,7 +65,7 @@ class Campaign < ActiveRecord::Base
|
|
63
65
|
uses_user_permissions
|
64
66
|
acts_as_commentable
|
65
67
|
acts_as_taggable_on :tags
|
66
|
-
has_paper_trail
|
68
|
+
has_paper_trail :ignore => [ :subscribed_users ]
|
67
69
|
has_fields
|
68
70
|
exportable
|
69
71
|
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"
|
@@ -60,6 +60,8 @@ class Contact < ActiveRecord::Base
|
|
60
60
|
has_one :business_address, :dependent => :destroy, :as => :addressable, :class_name => "Address", :conditions => "address_type = 'Business'"
|
61
61
|
has_many :emails, :as => :mediator
|
62
62
|
|
63
|
+
serialize :subscribed_users, Set
|
64
|
+
|
63
65
|
accepts_nested_attributes_for :business_address, :allow_destroy => true
|
64
66
|
|
65
67
|
scope :created_by, lambda { |user| { :conditions => [ "user_id = ?", user.id ] } }
|
@@ -82,7 +84,7 @@ class Contact < ActiveRecord::Base
|
|
82
84
|
uses_user_permissions
|
83
85
|
acts_as_commentable
|
84
86
|
acts_as_taggable_on :tags
|
85
|
-
has_paper_trail
|
87
|
+
has_paper_trail :ignore => [ :subscribed_users ]
|
86
88
|
has_fields
|
87
89
|
exportable
|
88
90
|
sortable :by => [ "first_name ASC", "last_name ASC", "created_at DESC", "updated_at DESC" ], :default => "created_at DESC"
|
data/app/models/entities/lead.rb
CHANGED
@@ -57,6 +57,8 @@ class Lead < ActiveRecord::Base
|
|
57
57
|
has_one :business_address, :dependent => :destroy, :as => :addressable, :class_name => "Address", :conditions => "address_type='Business'"
|
58
58
|
has_many :emails, :as => :mediator
|
59
59
|
|
60
|
+
serialize :subscribed_users, Set
|
61
|
+
|
60
62
|
accepts_nested_attributes_for :business_address, :allow_destroy => true
|
61
63
|
|
62
64
|
scope :state, lambda { |filters|
|
@@ -75,7 +77,7 @@ class Lead < ActiveRecord::Base
|
|
75
77
|
uses_user_permissions
|
76
78
|
acts_as_commentable
|
77
79
|
acts_as_taggable_on :tags
|
78
|
-
has_paper_trail
|
80
|
+
has_paper_trail :ignore => [ :subscribed_users ]
|
79
81
|
has_fields
|
80
82
|
exportable
|
81
83
|
sortable :by => [ "first_name ASC", "last_name ASC", "company ASC", "rating DESC", "created_at DESC", "updated_at DESC" ], :default => "created_at DESC"
|
@@ -125,7 +127,7 @@ class Lead < ActiveRecord::Base
|
|
125
127
|
opportunity = Opportunity.create_for(self, account, params[:opportunity], params[:users])
|
126
128
|
contact = Contact.create_for(self, account, opportunity, params)
|
127
129
|
|
128
|
-
|
130
|
+
[account, opportunity, contact]
|
129
131
|
end
|
130
132
|
|
131
133
|
#----------------------------------------------------------------------------
|
@@ -162,7 +164,8 @@ class Lead < ActiveRecord::Base
|
|
162
164
|
end
|
163
165
|
alias :name :full_name
|
164
166
|
|
165
|
-
|
167
|
+
private
|
168
|
+
|
166
169
|
#----------------------------------------------------------------------------
|
167
170
|
def increment_leads_count
|
168
171
|
if self.campaign_id
|
@@ -182,6 +185,4 @@ class Lead < ActiveRecord::Base
|
|
182
185
|
def users_for_shared_access
|
183
186
|
errors.add(:access, :share_lead) if self[:access] == "Shared" && !self.permissions.any?
|
184
187
|
end
|
185
|
-
|
186
188
|
end
|
187
|
-
|
@@ -48,6 +48,8 @@ class Opportunity < ActiveRecord::Base
|
|
48
48
|
has_many :tasks, :as => :asset, :dependent => :destroy#, :order => 'created_at DESC'
|
49
49
|
has_many :emails, :as => :mediator
|
50
50
|
|
51
|
+
serialize :subscribed_users, Set
|
52
|
+
|
51
53
|
scope :state, lambda { |filters|
|
52
54
|
where('stage IN (?)' + (filters.delete('other') ? ' OR stage IS NULL' : ''), filters)
|
53
55
|
}
|
@@ -71,7 +73,7 @@ class Opportunity < ActiveRecord::Base
|
|
71
73
|
uses_user_permissions
|
72
74
|
acts_as_commentable
|
73
75
|
acts_as_taggable_on :tags
|
74
|
-
has_paper_trail
|
76
|
+
has_paper_trail :ignore => [ :subscribed_users ]
|
75
77
|
has_fields
|
76
78
|
exportable
|
77
79
|
sortable :by => [ "name ASC", "amount DESC", "amount*probability DESC", "probability DESC", "closes_on ASC", "created_at DESC", "updated_at DESC" ], :default => "created_at DESC"
|
data/app/models/fields/field.rb
CHANGED
@@ -112,7 +112,7 @@ class Field < ActiveRecord::Base
|
|
112
112
|
when 'date'
|
113
113
|
value && value.strftime(I18n.t("date.formats.default"))
|
114
114
|
when 'datetime'
|
115
|
-
value && value.strftime(I18n.t("time.formats.
|
115
|
+
value && value.strftime(I18n.t("time.formats.long"))
|
116
116
|
when 'check_boxes'
|
117
117
|
value = YAML.load(value) if value.is_a?(String)
|
118
118
|
value.select(&:present?).in_groups_of(2, false).map {|g| g.join(', ')}.join("<br />".html_safe) if Array === value
|
@@ -41,6 +41,40 @@ class Comment < ActiveRecord::Base
|
|
41
41
|
has_paper_trail :meta => { :related => :commentable },
|
42
42
|
:ignore => [:state]
|
43
43
|
|
44
|
+
before_create :subscribe_mentioned_users
|
45
|
+
after_create :subscribe_user_to_entity, :notify_subscribers
|
46
|
+
|
44
47
|
def expanded?; self.state == "Expanded"; end
|
45
48
|
def collapsed?; self.state == "Collapsed"; end
|
49
|
+
|
50
|
+
private
|
51
|
+
# Add user to subscribed_users field on entity
|
52
|
+
def subscribe_user_to_entity(u = user)
|
53
|
+
commentable.subscribed_users << u.id
|
54
|
+
commentable.save
|
55
|
+
end
|
56
|
+
|
57
|
+
# Notify subscribed users when a comment is added, unless user created this comment
|
58
|
+
def notify_subscribers
|
59
|
+
commentable.subscribed_users.reject{|user_id| user_id == user.id}.each do |subscriber_id|
|
60
|
+
if subscriber = User.find_by_id(subscriber_id)
|
61
|
+
# Only send email if SMTP settings are configured
|
62
|
+
if Rails.application.config.action_mailer.smtp_settings.present?
|
63
|
+
SubscriptionMailer.comment_notification(subscriber, self).deliver
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# If a user is mentioned in the comment body, subscribe them to the entity
|
70
|
+
# before creation, so that they are sent an email notification
|
71
|
+
def subscribe_mentioned_users
|
72
|
+
# Scan for usernames mentioned in the comment,
|
73
|
+
# e.g. "Hi @example_user, take a look at this lead. Please show @another_user"
|
74
|
+
comment.scan(/@([a-zA-Z0-9_-]+)/).map(&:first).each do |username|
|
75
|
+
if (mentioned_user = User.find_by_username(username))
|
76
|
+
subscribe_user_to_entity(mentioned_user)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
46
80
|
end
|
@@ -45,6 +45,8 @@ class Task < ActiveRecord::Base
|
|
45
45
|
belongs_to :completor, :class_name => "User", :foreign_key => :completed_by
|
46
46
|
belongs_to :asset, :polymorphic => true
|
47
47
|
|
48
|
+
serialize :subscribed_users, Set
|
49
|
+
|
48
50
|
# Tasks created by the user for herself, or assigned to her by others. That's
|
49
51
|
# what gets shown on Tasks/Pending and Tasks/Completed pages.
|
50
52
|
scope :my, lambda { |*args|
|
@@ -97,7 +99,7 @@ class Task < ActiveRecord::Base
|
|
97
99
|
}
|
98
100
|
|
99
101
|
acts_as_commentable
|
100
|
-
has_paper_trail :meta => { :related => :asset }
|
102
|
+
has_paper_trail :meta => { :related => :asset }, :ignore => [ :subscribed_users ]
|
101
103
|
has_fields
|
102
104
|
exportable
|
103
105
|
|
@@ -231,10 +233,21 @@ class Task < ActiveRecord::Base
|
|
231
233
|
rescue ArgumentError
|
232
234
|
errors.add(:calendar, :invalid_date)
|
233
235
|
end
|
234
|
-
|
236
|
+
|
235
237
|
#----------------------------------------------------------------------------
|
236
238
|
def parse_calendar_date
|
237
|
-
|
239
|
+
translate_month_and_day_names!(self.calendar) unless I18n.locale == :"en-US"
|
240
|
+
|
241
|
+
DateTime.strptime(self.calendar,
|
242
|
+
I18n.t(Setting.task_calendar_with_time ? 'time.formats.mmddyyyy_hhmm' : 'date.formats.mmddyyyy')).utc
|
243
|
+
end
|
244
|
+
|
245
|
+
# Translates month and day names of a given datetime string.
|
246
|
+
#----------------------------------------------------------------------------
|
247
|
+
def translate_month_and_day_names!(date_string)
|
248
|
+
translated = I18n.t([:month_names, :abbr_month_names, :day_names, :abbr_day_names], :scope => :date).flatten.compact
|
249
|
+
original = (Date::MONTHNAMES + Date::ABBR_MONTHNAMES + Date::DAYNAMES + Date::ABBR_DAYNAMES).compact
|
250
|
+
translated.each_with_index { |name, i| date_string.gsub!(name, original[i]) }
|
238
251
|
end
|
239
252
|
end
|
240
253
|
|
data/app/models/setting.rb
CHANGED
@@ -27,7 +27,7 @@
|
|
27
27
|
#
|
28
28
|
|
29
29
|
# Fat Free CRM settings are stored in three places, and are loaded in the following order:
|
30
|
-
#
|
30
|
+
#
|
31
31
|
# 1) config/settings.default.yml
|
32
32
|
# 2) config/settings.yml (if exists)
|
33
33
|
# 3) 'settings' table in database (if exists)
|
@@ -44,16 +44,16 @@ class Setting < ActiveRecord::Base
|
|
44
44
|
@@cache = @@yaml_settings = {}.with_indifferent_access
|
45
45
|
|
46
46
|
class << self
|
47
|
-
|
47
|
+
|
48
48
|
# Cache should be cleared before each request.
|
49
49
|
def clear_cache!
|
50
50
|
@@cache = {}.with_indifferent_access
|
51
51
|
end
|
52
|
-
|
52
|
+
|
53
53
|
#-------------------------------------------------------------------
|
54
54
|
def method_missing(method, *args)
|
55
55
|
begin
|
56
|
-
super
|
56
|
+
super
|
57
57
|
rescue NoMethodError
|
58
58
|
method_name = method.to_s
|
59
59
|
if method_name.last == "="
|
@@ -70,30 +70,30 @@ class Setting < ActiveRecord::Base
|
|
70
70
|
# Return value if cached
|
71
71
|
return cache[name] if cache.has_key?(name)
|
72
72
|
# Check database
|
73
|
-
if database_and_table_exists?
|
74
|
-
if setting = self.find_by_name(name)
|
73
|
+
if database_and_table_exists?
|
74
|
+
if setting = self.find_by_name(name.to_s)
|
75
75
|
unless setting.value.nil?
|
76
76
|
return cache[name] = setting.value
|
77
77
|
end
|
78
78
|
end
|
79
79
|
end
|
80
|
-
# Check YAML settings
|
80
|
+
# Check YAML settings
|
81
81
|
if yaml_settings.has_key?(name)
|
82
82
|
return cache[name] = yaml_settings[name]
|
83
83
|
end
|
84
84
|
end
|
85
|
-
|
85
|
+
|
86
86
|
|
87
87
|
# Set setting value
|
88
88
|
#-------------------------------------------------------------------
|
89
89
|
def []=(name, value)
|
90
90
|
return nil unless database_and_table_exists?
|
91
|
-
setting = self.find_by_name(name) || self.new(:name => name)
|
91
|
+
setting = self.find_by_name(name.to_s) || self.new(:name => name)
|
92
92
|
setting.value = value
|
93
93
|
setting.save
|
94
94
|
cache[name] = value
|
95
95
|
end
|
96
|
-
|
96
|
+
|
97
97
|
|
98
98
|
# Unrolls [ :one, :two ] settings array into [[ "One", :one ], [ "Two", :two ]]
|
99
99
|
# picking symbol translations from locale. If setting is not a symbol but
|
@@ -109,12 +109,12 @@ class Setting < ActiveRecord::Base
|
|
109
109
|
# instead of crashing the entire application.
|
110
110
|
table_exists? rescue false
|
111
111
|
end
|
112
|
-
|
113
|
-
|
112
|
+
|
113
|
+
|
114
114
|
# Loads settings from YAML files
|
115
115
|
def load_settings_from_yaml
|
116
116
|
@@yaml_settings = {}.with_indifferent_access
|
117
|
-
|
117
|
+
|
118
118
|
setting_files = [
|
119
119
|
FatFreeCRM.root.join("config", "settings.default.yml"),
|
120
120
|
Rails.root.join("config", "settings.yml")
|
@@ -125,8 +125,8 @@ class Setting < ActiveRecord::Base
|
|
125
125
|
if File.exist?(file)
|
126
126
|
begin
|
127
127
|
settings = YAML.load_file(file)
|
128
|
-
# Merge settings into current settings hash
|
129
|
-
@@yaml_settings.
|
128
|
+
# Merge settings into current settings hash (recursively)
|
129
|
+
@@yaml_settings.deep_merge!(settings)
|
130
130
|
rescue Exception => ex
|
131
131
|
puts "Settings couldn't be loaded from #{file}: #{ex.message}"
|
132
132
|
end
|
data/app/models/users/ability.rb
CHANGED
@@ -1,13 +1,20 @@
|
|
1
|
+
# See the wiki for details: https://github.com/ryanb/cancan/wiki/Defining-Abilities
|
2
|
+
|
1
3
|
class Ability
|
2
4
|
include CanCan::Ability
|
3
5
|
|
4
6
|
def initialize(user)
|
5
|
-
# See the wiki for details: https://github.com/ryanb/cancan/wiki/Defining-Abilities
|
6
|
-
can :create, :all
|
7
|
-
can [:read, :update, :destroy], :all, :access => 'Public'
|
8
7
|
if user.present?
|
9
|
-
|
10
|
-
|
8
|
+
entities = [Account, Campaign, Contact, Lead, Opportunity]
|
9
|
+
|
10
|
+
can :create, :all
|
11
|
+
can :manage, entities, :access => 'Public'
|
12
|
+
can :manage, entities + [Task], :user_id => user.id
|
13
|
+
|
14
|
+
entities.each do |klass|
|
15
|
+
permissions = user.permissions.where(:asset_type => klass.name)
|
16
|
+
can :manage, klass, :id => permissions.map(&:asset_id)
|
17
|
+
end
|
11
18
|
end
|
12
19
|
end
|
13
20
|
end
|
data/app/models/users/user.rb
CHANGED
@@ -80,11 +80,16 @@ class User < ActiveRecord::Base
|
|
80
80
|
where('upper(username) LIKE upper(:s) OR upper(first_name) LIKE upper(:s) OR upper(last_name) LIKE upper(:s)', :s => "#{query}%")
|
81
81
|
}
|
82
82
|
|
83
|
+
scope :my, lambda {
|
84
|
+
current_ability = Ability.new(User.current_user)
|
85
|
+
accessible_by(current_ability)
|
86
|
+
}
|
87
|
+
|
83
88
|
acts_as_authentic do |c|
|
84
89
|
c.session_class = Authentication
|
85
90
|
c.validates_uniqueness_of_login_field_options = { :message => :username_taken }
|
86
91
|
c.validates_length_of_login_field_options = { :minimum => 1, :message => :missing_username }
|
87
|
-
c.merge_validates_format_of_login_field_options(:with =>
|
92
|
+
c.merge_validates_format_of_login_field_options(:with => /[a-zA-Z0-9_-]+/)
|
88
93
|
|
89
94
|
c.validates_uniqueness_of_email_field_options = { :message => :email_in_use }
|
90
95
|
c.validates_length_of_password_field_options = { :minimum => 0, :allow_blank => true, :if => :require_password? }
|
@@ -126,7 +131,7 @@ class User < ActiveRecord::Base
|
|
126
131
|
#----------------------------------------------------------------------------
|
127
132
|
def deliver_password_reset_instructions!
|
128
133
|
reset_perishable_token!
|
129
|
-
|
134
|
+
UserMailer.password_reset_instructions(self).deliver
|
130
135
|
end
|
131
136
|
|
132
137
|
# Override global I18n.locale if the user has individual local preference.
|
@@ -11,7 +11,7 @@
|
|
11
11
|
= image_tag("loading.gif", :size => :thumb, :id => "loading", :style => "display: none;")
|
12
12
|
.remote#options{ hidden }
|
13
13
|
.remote#advanced_search{ hidden_if(!params[:q]) }
|
14
|
-
- if
|
14
|
+
- if params[:q]
|
15
15
|
= render :partial => "advanced_search"
|
16
16
|
.remote#create_account{ hidden }
|
17
17
|
|
File without changes
|
@@ -11,7 +11,7 @@ end
|
|
11
11
|
atom_feed do |feed|
|
12
12
|
feed.title title || t(items.to_sym)
|
13
13
|
feed.updated assets.any? ? assets.max { |a, b| a.updated_at <=> b.updated_at }.updated_at : Time.now
|
14
|
-
feed.generator "Fat Free CRM v#{FatFreeCRM::
|
14
|
+
feed.generator "Fat Free CRM v#{FatFreeCRM::VERSION::STRING}"
|
15
15
|
feed.author do |author|
|
16
16
|
author.name @current_user.full_name
|
17
17
|
author.email @current_user.email
|
@@ -11,7 +11,7 @@ end
|
|
11
11
|
xml.instruct! :xml, :version => "1.0"
|
12
12
|
xml.rss :version => "2.0" do
|
13
13
|
xml.channel do
|
14
|
-
xml.generator "Fat Free CRM v#{FatFreeCRM::
|
14
|
+
xml.generator "Fat Free CRM v#{FatFreeCRM::VERSION::STRING}"
|
15
15
|
xml.link send(:"#{items}_url")
|
16
16
|
xml.pubDate Time.now.to_s(:rfc822)
|
17
17
|
xml.title title || t(items.to_sym)
|
@@ -11,7 +11,7 @@
|
|
11
11
|
= image_tag("loading.gif", :size => :thumb, :id => "loading", :style => "display: none;")
|
12
12
|
.remote#options{ hidden }
|
13
13
|
.remote#advanced_search{ hidden_if(!params[:q]) }
|
14
|
-
- if
|
14
|
+
- if params[:q]
|
15
15
|
= render :partial => "advanced_search"
|
16
16
|
.remote#create_campaign{ hidden }
|
17
17
|
|
@@ -1,6 +1,10 @@
|
|
1
1
|
- class_name = commentable.class.name.downcase
|
2
2
|
- id_prefix = "#{class_name}_#{commentable.id}"
|
3
3
|
.comment.highlight.new_comment{ :id => "#{id_prefix}_comment_new" }
|
4
|
+
- subscribed_users = commentable.subscribed_users.map{|uid| User.find_by_id(uid) unless uid == @current_user.id }.compact
|
5
|
+
- if notification_emails_configured? && subscribed_users.any?
|
6
|
+
= t(:following_users_will_be_notified) << ":"
|
7
|
+
= subscribed_user_links(subscribed_users)
|
4
8
|
|
5
9
|
-# Two hidden fields store the IDs of notes and emails shown for the asset. These IDs are used
|
6
10
|
-# by [Expand/Collapse All]. The contents gets updated by actions such as [Add] or [Delete].
|
@@ -25,3 +29,5 @@
|
|
25
29
|
%div{ {:id => "#{id_prefix}_ask"}.merge(hidden_if(false))}
|
26
30
|
= text_field_tag :post_new_note, t(:add_note_help), :onclick => remote_function(:url => new_comment_path("#{class_name}_id" => commentable), :method => :get), :id => "#{id_prefix}_post_new_note"
|
27
31
|
|
32
|
+
- if notification_emails_configured?
|
33
|
+
= render :partial => "comments/subscription_links", :locals => {:entity => commentable}
|
@@ -0,0 +1,13 @@
|
|
1
|
+
- class_name = entity.class.name.downcase
|
2
|
+
- id_prefix = "#{class_name}_#{entity.id}"
|
3
|
+
|
4
|
+
- subscribed = entity.subscribed_users.include?(current_user.id)
|
5
|
+
|
6
|
+
- if subscribed
|
7
|
+
- link_text, action = [t(:disable_email_subscriptions), "unsubscribe"]
|
8
|
+
- else
|
9
|
+
- link_text, action = [t(:subscribe_via_email), "subscribe"]
|
10
|
+
|
11
|
+
%div{:id => "#{id_prefix}_subscribe", :class => "comment_subscriptions"}
|
12
|
+
= image_tag "notifications.png", :title => t(:notifications_tooltip)
|
13
|
+
= link_to link_text, url_for(:controller => class_name.pluralize, :action => action, :id => entity.id), :remote => true, :method => :post
|