fat_free_crm 0.11.1 → 0.11.2
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.
- 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
|