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
@@ -1,24 +0,0 @@
|
|
1
|
-
en:
|
2
|
-
simple_form:
|
3
|
-
"yes": 'Yes'
|
4
|
-
"no": 'No'
|
5
|
-
required:
|
6
|
-
text: 'required'
|
7
|
-
mark: '*'
|
8
|
-
# You can uncomment the line below if you need to overwrite the whole required html.
|
9
|
-
# When using html, text and mark won't be used.
|
10
|
-
# html: '<abbr title="required">*</abbr>'
|
11
|
-
error_notification:
|
12
|
-
default_message: "Some errors were found, please take a look:"
|
13
|
-
# Labels and hints examples
|
14
|
-
# labels:
|
15
|
-
# password: 'Password'
|
16
|
-
# user:
|
17
|
-
# new:
|
18
|
-
# email: 'E-mail para efetuar o sign in.'
|
19
|
-
# edit:
|
20
|
-
# email: 'E-mail.'
|
21
|
-
# hints:
|
22
|
-
# username: 'User name to sign in.'
|
23
|
-
# password: 'No special characters, please.'
|
24
|
-
|
data/lib/fat_free_crm/dropbox.rb
DELETED
@@ -1,439 +0,0 @@
|
|
1
|
-
# Fat Free CRM
|
2
|
-
# Copyright (C) 2008-2011 by Michael Dvorkin
|
3
|
-
#
|
4
|
-
# This program is free software: you can redistribute it and/or modify
|
5
|
-
# it under the terms of the GNU Affero General Public License as published by
|
6
|
-
# the Free Software Foundation, either version 3 of the License, or
|
7
|
-
# (at your option) any later version.
|
8
|
-
#
|
9
|
-
# This program is distributed in the hope that it will be useful,
|
10
|
-
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
-
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
-
# GNU Affero General Public License for more details.
|
13
|
-
#
|
14
|
-
# You should have received a copy of the GNU Affero General Public License
|
15
|
-
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
16
|
-
#------------------------------------------------------------------------------
|
17
|
-
|
18
|
-
require "net/imap"
|
19
|
-
require "mail"
|
20
|
-
|
21
|
-
module FatFreeCRM
|
22
|
-
class Dropbox
|
23
|
-
KEYWORDS = %w(account campaign contact lead opportunity).freeze
|
24
|
-
|
25
|
-
#--------------------------------------------------------------------------------------
|
26
|
-
def initialize
|
27
|
-
# Models are autoloaded, so the following @@assets class variable should only be set
|
28
|
-
# when Dropbox is initialized. This needs to be done so that Rake tasks such as
|
29
|
-
# 'assets:precompile' can run on Heroku without depending on a database.
|
30
|
-
# See: http://devcenter.heroku.com/articles/rails31_heroku_cedar#troubleshooting
|
31
|
-
@@assets = [ Account, Contact, Lead ].freeze
|
32
|
-
@settings = Setting.email_dropbox.dup
|
33
|
-
@archived, @discarded = 0, 0
|
34
|
-
end
|
35
|
-
|
36
|
-
#--------------------------------------------------------------------------------------
|
37
|
-
def run
|
38
|
-
log "connecting to #{@settings[:server]}..."
|
39
|
-
connect! or return nil
|
40
|
-
log "logged in to #{@settings[:server]}..."
|
41
|
-
with_new_emails do |uid, email|
|
42
|
-
process(uid, email)
|
43
|
-
archive(uid)
|
44
|
-
end
|
45
|
-
expunge!
|
46
|
-
ensure
|
47
|
-
log "messages processed: #{@archived + @discarded}, archived: #{@archived}, discarded: #{@discarded}."
|
48
|
-
disconnect!
|
49
|
-
end
|
50
|
-
|
51
|
-
# Setup imap folders in settings.
|
52
|
-
#--------------------------------------------------------------------------------------
|
53
|
-
def setup
|
54
|
-
log "connecting to #{@settings[:server]}..."
|
55
|
-
connect!(:setup => true) or return nil
|
56
|
-
log "logged in to #{@settings[:server]}, checking folders..."
|
57
|
-
folders = [ @settings[:scan_folder] ]
|
58
|
-
folders << @settings[:move_to_folder] unless @settings[:move_to_folder].blank?
|
59
|
-
folders << @settings[:move_invalid_to_folder] unless @settings[:move_invalid_to_folder].blank?
|
60
|
-
|
61
|
-
# Open (or create) destination folder in read-write mode.
|
62
|
-
folders.each do |folder|
|
63
|
-
if @imap.list("", folder)
|
64
|
-
log "folder #{folder} OK"
|
65
|
-
else
|
66
|
-
log "folder #{folder} missing, creating..."
|
67
|
-
@imap.create(folder)
|
68
|
-
end
|
69
|
-
end
|
70
|
-
rescue => e
|
71
|
-
$stderr.puts "setup error #{e.inspect}"
|
72
|
-
ensure
|
73
|
-
disconnect!
|
74
|
-
end
|
75
|
-
|
76
|
-
private
|
77
|
-
|
78
|
-
#--------------------------------------------------------------------------------------
|
79
|
-
def with_new_emails
|
80
|
-
@imap.uid_search(['NOT', 'SEEN']).each do |uid|
|
81
|
-
begin
|
82
|
-
email = Mail.new(@imap.uid_fetch(uid, 'RFC822').first.attr['RFC822'])
|
83
|
-
log "fetched new message...", email
|
84
|
-
if is_valid?(email) && sent_from_known_user?(email)
|
85
|
-
yield(uid, email)
|
86
|
-
else
|
87
|
-
discard(uid)
|
88
|
-
end
|
89
|
-
rescue Exception => e
|
90
|
-
if ["test", "development"].include?(Rails.env)
|
91
|
-
$stderr.puts e
|
92
|
-
$stderr.puts e.backtrace
|
93
|
-
end
|
94
|
-
log "error processing email: #{e.inspect}", email
|
95
|
-
discard(uid)
|
96
|
-
end
|
97
|
-
end
|
98
|
-
end
|
99
|
-
|
100
|
-
# Email processing pipeline: each steps gets executed if previous one returns false.
|
101
|
-
#--------------------------------------------------------------------------------------
|
102
|
-
def process(uid, email)
|
103
|
-
with_explicit_keyword(email) do |keyword, name|
|
104
|
-
data = {"Type" => keyword, "Name" => name}
|
105
|
-
find_or_create_and_attach(email, data)
|
106
|
-
end and return
|
107
|
-
|
108
|
-
with_recipients(email) do |recipient|
|
109
|
-
find_and_attach(email, recipient)
|
110
|
-
end and return
|
111
|
-
|
112
|
-
with_forwarded_recipient(email) do |recipient|
|
113
|
-
find_and_attach(email, recipient)
|
114
|
-
end and return
|
115
|
-
|
116
|
-
with_recipients(email) do |recipient|
|
117
|
-
create_and_attach(email, recipient)
|
118
|
-
end and return
|
119
|
-
|
120
|
-
with_forwarded_recipient(email) do |recipient|
|
121
|
-
create_and_attach(email, recipient)
|
122
|
-
end
|
123
|
-
end
|
124
|
-
|
125
|
-
#------------------------------------------------------------------------------
|
126
|
-
def expunge!
|
127
|
-
if @imap
|
128
|
-
# Sends a EXPUNGE command to permanently remove from the currently selected mailbox
|
129
|
-
# all messages that have the Deleted flag set.
|
130
|
-
@imap.expunge
|
131
|
-
end
|
132
|
-
end
|
133
|
-
|
134
|
-
# Connects to the imap server with the loaded settings
|
135
|
-
#------------------------------------------------------------------------------
|
136
|
-
def connect!(options = {})
|
137
|
-
@imap = Net::IMAP.new(@settings[:server], @settings[:port], @settings[:ssl])
|
138
|
-
@imap.login(@settings[:user], @settings[:password])
|
139
|
-
@imap.select(@settings[:scan_folder]) unless options[:setup]
|
140
|
-
@imap
|
141
|
-
rescue Exception => e
|
142
|
-
$stderr.puts "Dropbox: could not login to the IMAP server: #{e.inspect}" unless Rails.env == "test"
|
143
|
-
nil
|
144
|
-
end
|
145
|
-
|
146
|
-
#------------------------------------------------------------------------------
|
147
|
-
def disconnect!
|
148
|
-
if @imap
|
149
|
-
@imap.logout
|
150
|
-
unless @imap.disconnected?
|
151
|
-
@imap.disconnect rescue nil
|
152
|
-
end
|
153
|
-
end
|
154
|
-
end
|
155
|
-
|
156
|
-
# Discard message (not valid) action based on settings
|
157
|
-
#------------------------------------------------------------------------------
|
158
|
-
def discard(uid)
|
159
|
-
if @settings[:move_invalid_to_folder]
|
160
|
-
@imap.uid_copy(uid, @settings[:move_invalid_to_folder])
|
161
|
-
end
|
162
|
-
@imap.uid_store(uid, "+FLAGS", [:Deleted])
|
163
|
-
@discarded += 1
|
164
|
-
end
|
165
|
-
|
166
|
-
# Archive message (valid) action based on settings
|
167
|
-
#------------------------------------------------------------------------------
|
168
|
-
def archive(uid)
|
169
|
-
if @settings[:move_to_folder]
|
170
|
-
@imap.uid_copy(uid, @settings[:move_to_folder])
|
171
|
-
end
|
172
|
-
@imap.uid_store(uid, "+FLAGS", [:Seen])
|
173
|
-
@archived += 1
|
174
|
-
end
|
175
|
-
|
176
|
-
#------------------------------------------------------------------------------
|
177
|
-
def is_valid?(email)
|
178
|
-
valid = email.content_type != "text/html"
|
179
|
-
log("not a text message, discarding") unless valid
|
180
|
-
valid
|
181
|
-
end
|
182
|
-
|
183
|
-
#------------------------------------------------------------------------------
|
184
|
-
def sent_from_known_user?(email)
|
185
|
-
email_address = email.from.first
|
186
|
-
known = !find_sender(email_address).nil?
|
187
|
-
log("sent by unknown user #{email_address}, discarding") unless known
|
188
|
-
known
|
189
|
-
end
|
190
|
-
|
191
|
-
#------------------------------------------------------------------------------
|
192
|
-
def find_sender(email_address)
|
193
|
-
@sender = User.first(:conditions => [ "(lower(email) = ? OR lower(alt_email) = ?) AND suspended_at IS NULL", email_address.downcase, email_address.downcase ])
|
194
|
-
end
|
195
|
-
|
196
|
-
# Checks the email to detect keyword on the first line.
|
197
|
-
#--------------------------------------------------------------------------------------
|
198
|
-
def with_explicit_keyword(email)
|
199
|
-
first_line = plain_text_body(email).split("\n").first
|
200
|
-
if first_line =~ %r|^[\./]?(#{KEYWORDS.join('|')})\s(.+)$|i
|
201
|
-
yield $1.capitalize, $2.strip
|
202
|
-
end
|
203
|
-
end
|
204
|
-
|
205
|
-
# Checks the email to detect assets on to/bcc addresses
|
206
|
-
#--------------------------------------------------------------------------------------
|
207
|
-
def with_recipients(email, options = {})
|
208
|
-
recipients = []
|
209
|
-
recipients += email.to_addrs unless email.to.blank?
|
210
|
-
recipients += email.cc_addrs unless email.cc.blank?
|
211
|
-
recipients -= [ @settings[:address] ]
|
212
|
-
recipients.inject(false) { |attached, recipient| attached ||= yield recipient }
|
213
|
-
end
|
214
|
-
|
215
|
-
# Checks the email to detect valid email address in body (first email), detect forwarded emails
|
216
|
-
#----------------------------------------------------------------------------------------
|
217
|
-
def with_forwarded_recipient(email, options = {})
|
218
|
-
if plain_text_body(email) =~ /\b([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4})\b/
|
219
|
-
yield $1
|
220
|
-
end
|
221
|
-
end
|
222
|
-
|
223
|
-
# Process pipe_separated_data or explicit keyword.
|
224
|
-
#--------------------------------------------------------------------------------------
|
225
|
-
def find_or_create_and_attach(email, data)
|
226
|
-
klass = data["Type"].constantize
|
227
|
-
|
228
|
-
if data["Email"] && klass.new.respond_to?(:email)
|
229
|
-
conditions = ['email = ?', data["Email"]]
|
230
|
-
elsif klass.new.respond_to?(:first_name)
|
231
|
-
first_name, *last_name = data["Name"].split
|
232
|
-
conditions = if last_name.empty? # Treat single name as last name.
|
233
|
-
[ 'last_name LIKE ?', "%#{first_name}" ]
|
234
|
-
else
|
235
|
-
[ 'first_name LIKE ? AND last_name LIKE ?', "%#{first_name}", "%#{last_name.join(' ')}" ]
|
236
|
-
end
|
237
|
-
else
|
238
|
-
conditions = ['name LIKE ?', "%#{data["Name"]}%"]
|
239
|
-
end
|
240
|
-
|
241
|
-
# Find the asset from deduced conditions
|
242
|
-
if asset = klass.where(conditions).first
|
243
|
-
if sender_has_permissions_for?(asset)
|
244
|
-
attach(email, asset, :strip_first_line)
|
245
|
-
else
|
246
|
-
log "Sender does not have permissions to attach email to #{data["Type"]} #{data["Email"]} <#{data["Name"]}>"
|
247
|
-
end
|
248
|
-
else
|
249
|
-
log "#{data["Type"]} #{data["Email"]} <#{data["Name"]}> not found, creating new one..."
|
250
|
-
asset = klass.create!(default_values(klass, data))
|
251
|
-
attach(email, asset, :strip_first_line)
|
252
|
-
end
|
253
|
-
true
|
254
|
-
end
|
255
|
-
|
256
|
-
#----------------------------------------------------------------------------------------
|
257
|
-
def find_and_attach(email, recipient)
|
258
|
-
attached = false
|
259
|
-
@@assets.each do |klass|
|
260
|
-
asset = klass.find_by_email(recipient)
|
261
|
-
|
262
|
-
# Leads and Contacts have an alt_email: try it if lookup by primary email has failed.
|
263
|
-
if !asset && klass.column_names.include?("alt_email")
|
264
|
-
asset = klass.find_by_alt_email(recipient)
|
265
|
-
end
|
266
|
-
|
267
|
-
if asset && sender_has_permissions_for?(asset)
|
268
|
-
attach(email, asset)
|
269
|
-
attached = true
|
270
|
-
end
|
271
|
-
end
|
272
|
-
attached
|
273
|
-
end
|
274
|
-
|
275
|
-
#----------------------------------------------------------------------------------------
|
276
|
-
def create_and_attach(email, recipient)
|
277
|
-
contact = Contact.create!(default_values_for_contact(email, recipient))
|
278
|
-
attach(email, contact)
|
279
|
-
end
|
280
|
-
|
281
|
-
#----------------------------------------------------------------------------------------
|
282
|
-
def attach(email, asset, strip_first_line=false)
|
283
|
-
to = email.to.blank? ? nil : email.to.join(", ")
|
284
|
-
cc = email.cc.blank? ? nil : email.cc.join(", ")
|
285
|
-
|
286
|
-
email_body = if strip_first_line
|
287
|
-
plain_text_body(email).split("\n")[1..-1].join("\n").strip
|
288
|
-
else
|
289
|
-
plain_text_body(email)
|
290
|
-
end
|
291
|
-
|
292
|
-
Email.create(
|
293
|
-
:imap_message_id => email.message_id,
|
294
|
-
:user => @sender,
|
295
|
-
:mediator => asset,
|
296
|
-
:sent_from => email.from.first,
|
297
|
-
:sent_to => to,
|
298
|
-
:cc => cc,
|
299
|
-
:subject => email.subject,
|
300
|
-
:body => email_body,
|
301
|
-
:received_at => email.date,
|
302
|
-
:sent_at => email.date
|
303
|
-
)
|
304
|
-
asset.touch
|
305
|
-
|
306
|
-
if asset.is_a?(Lead) && asset.status == "new"
|
307
|
-
asset.update_attribute(:status, "contacted")
|
308
|
-
end
|
309
|
-
|
310
|
-
if @settings[:attach_to_account] && asset.respond_to?(:account) && asset.account
|
311
|
-
Email.create(
|
312
|
-
:imap_message_id => email.message_id,
|
313
|
-
:user => @sender,
|
314
|
-
:mediator => asset.account,
|
315
|
-
:sent_from => email.from.first,
|
316
|
-
:sent_to => to,
|
317
|
-
:cc => cc,
|
318
|
-
:subject => email.subject,
|
319
|
-
:body => email_body,
|
320
|
-
:received_at => email.date,
|
321
|
-
:sent_at => email.date
|
322
|
-
)
|
323
|
-
asset.account.touch
|
324
|
-
end
|
325
|
-
end
|
326
|
-
|
327
|
-
#----------------------------------------------------------------------------------------
|
328
|
-
def default_values(klass, data)
|
329
|
-
data = data.dup
|
330
|
-
keyword = data.delete("Type").capitalize
|
331
|
-
|
332
|
-
defaults = {
|
333
|
-
:user => @sender,
|
334
|
-
:access => default_access
|
335
|
-
}
|
336
|
-
|
337
|
-
case keyword
|
338
|
-
when "Account", "Campaign", "Opportunity"
|
339
|
-
defaults[:status] = "planned" if keyword == "Campaign" # TODO: I18n
|
340
|
-
defaults[:stage] = "prospecting" if keyword == "Opportunity" # TODO: I18n
|
341
|
-
|
342
|
-
when "Contact", "Lead"
|
343
|
-
first_name, *last_name = data.delete("Name").split(' ')
|
344
|
-
defaults[:first_name] = first_name
|
345
|
-
defaults[:last_name] = (last_name.any? ? last_name.join(" ") : "(unknown)")
|
346
|
-
defaults[:status] = "contacted" if keyword == "Lead" # TODO: I18n
|
347
|
-
end
|
348
|
-
|
349
|
-
data.each do |key, value|
|
350
|
-
key = key.downcase
|
351
|
-
defaults[key.to_sym] = value if klass.new.respond_to?(key + '=')
|
352
|
-
end
|
353
|
-
|
354
|
-
defaults
|
355
|
-
end
|
356
|
-
|
357
|
-
#----------------------------------------------------------------------------------------
|
358
|
-
def default_values_for_contact(email, recipient)
|
359
|
-
recipient_local, recipient_domain = recipient.split('@')
|
360
|
-
|
361
|
-
defaults = {
|
362
|
-
:user => @sender,
|
363
|
-
:first_name => recipient_local.capitalize,
|
364
|
-
:last_name => "(unknown)",
|
365
|
-
:email => recipient,
|
366
|
-
:access => default_access
|
367
|
-
}
|
368
|
-
|
369
|
-
# Search for domain name in Accounts.
|
370
|
-
account = Account.where('email like ?', "%#{recipient_domain}").first
|
371
|
-
if account
|
372
|
-
log "asociating new contact #{recipient} with the account #{account.name}"
|
373
|
-
defaults[:account] = account
|
374
|
-
else
|
375
|
-
log "creating new account #{recipient_domain.capitalize} for the contact #{recipient}"
|
376
|
-
defaults[:account] = Account.create!(
|
377
|
-
:user => @sender,
|
378
|
-
:email => recipient,
|
379
|
-
:name => recipient_domain.capitalize,
|
380
|
-
:access => default_access
|
381
|
-
)
|
382
|
-
end
|
383
|
-
defaults
|
384
|
-
end
|
385
|
-
|
386
|
-
# If default access is 'Shared' then change it to 'Private' because we don't know how
|
387
|
-
# to choose anyone to share it with here.
|
388
|
-
#--------------------------------------------------------------------------------------
|
389
|
-
def default_access
|
390
|
-
Setting.default_access == "Shared" ? 'Private' : Setting.default_access
|
391
|
-
end
|
392
|
-
|
393
|
-
#--------------------------------------------------------------------------------------
|
394
|
-
def sender_has_permissions_for?(asset)
|
395
|
-
return true if asset.access == "Public"
|
396
|
-
return true if asset.user_id == @sender.id || asset.assigned_to == @sender.id
|
397
|
-
return true if asset.access == "Shared" && Permission.where('user_id = ? AND asset_id = ? AND asset_type = ?', @sender.id, asset.id, asset.class.to_s).count > 0
|
398
|
-
|
399
|
-
false
|
400
|
-
end
|
401
|
-
|
402
|
-
# Notify users with the results of the operations (feedback from dropbox)
|
403
|
-
#--------------------------------------------------------------------------------------
|
404
|
-
def notify(email, mediator_links)
|
405
|
-
ack_email = Notifier.create_dropbox_notification(@sender, @settings[:address], email, mediator_links)
|
406
|
-
Notifier.deliver(ack_email)
|
407
|
-
end
|
408
|
-
|
409
|
-
# Centralized logging.
|
410
|
-
#--------------------------------------------------------------------------------------
|
411
|
-
def log(message, email = nil)
|
412
|
-
return if Rails.env == "test"
|
413
|
-
puts "Dropbox: #{message}"
|
414
|
-
puts " From: #{email.from}, Subject: #{email.subject} (#{email.message_id})" if email
|
415
|
-
end
|
416
|
-
|
417
|
-
# Returns the plain-text version of an email, or strips html tags
|
418
|
-
# if only html is present.
|
419
|
-
#--------------------------------------------------------------------------------------
|
420
|
-
def plain_text_body(email)
|
421
|
-
parts = email.parts.collect {|c| (c.respond_to?(:parts) && !c.parts.empty?) ? c.parts : c}.flatten
|
422
|
-
if parts.empty?
|
423
|
-
parts << email
|
424
|
-
end
|
425
|
-
plain_text_part = parts.detect {|p| p.content_type.include?('text/plain')}
|
426
|
-
if plain_text_part.nil?
|
427
|
-
# no text/plain part found, assuming html-only email
|
428
|
-
# strip html tags and remove doctype directive
|
429
|
-
plain_text_body = email.body.to_s.gsub(/<\/?[^>]*>/, "")
|
430
|
-
plain_text_body.gsub! %r{^<!DOCTYPE .*$}, ''
|
431
|
-
else
|
432
|
-
plain_text_body = plain_text_part.body.to_s
|
433
|
-
end
|
434
|
-
plain_text_body.strip.gsub("\r\n", "\n")
|
435
|
-
end
|
436
|
-
|
437
|
-
end # class Dropbox
|
438
|
-
end # module FatFreeCRM
|
439
|
-
|