mail_manager 3.0.0 → 3.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (204) hide show
  1. checksums.yaml +5 -13
  2. data/.gitignore +5 -0
  3. data/.rspec +1 -0
  4. data/.ruby-version +1 -1
  5. data/Gemfile +27 -10
  6. data/LICENSE.txt +2 -2
  7. data/MIT-LICENSE +1 -1
  8. data/README.md +97 -15
  9. data/{features/bounce_management.feature → app/assets/images/mail_manager/.gitkeep} +0 -0
  10. data/app/assets/images/mail_manager/BottomRight.gif +0 -0
  11. data/app/assets/images/mail_manager/MidRight.gif +0 -0
  12. data/app/assets/images/mail_manager/TopCenter.gif +0 -0
  13. data/app/assets/images/mail_manager/TopRight.gif +0 -0
  14. data/app/assets/images/mail_manager/calendar_date_select/calendar.gif +0 -0
  15. data/app/assets/images/mail_manager/iReach_logo.gif +0 -0
  16. data/app/assets/images/mail_manager/spacer.gif +0 -0
  17. data/app/assets/images/mail_manager/topMid.gif +0 -0
  18. data/app/assets/javascripts/mail_manager/application.js +19 -1
  19. data/app/assets/javascripts/mail_manager/jquery-ui-timepicker-addon.js +2134 -0
  20. data/app/assets/stylesheets/mail_manager/admin.css +261 -0
  21. data/app/assets/stylesheets/mail_manager/application.css +3 -1
  22. data/app/assets/stylesheets/mail_manager/nav.css +68 -0
  23. data/app/assets/stylesheets/mail_manager/timepicker.css +11 -0
  24. data/app/controllers/mail_manager/application_controller.rb +7 -2
  25. data/app/controllers/mail_manager/bounces_controller.rb +5 -10
  26. data/app/controllers/mail_manager/contacts_controller.rb +42 -20
  27. data/app/controllers/mail_manager/mailing_lists_controller.rb +5 -12
  28. data/app/controllers/mail_manager/mailings_controller.rb +18 -18
  29. data/app/controllers/mail_manager/messages_controller.rb +3 -10
  30. data/app/controllers/mail_manager/subscriptions_controller.rb +16 -75
  31. data/app/helpers/mail_manager/layout_helper.rb +43 -0
  32. data/app/models/mail_manager/bounce.rb +16 -5
  33. data/app/models/mail_manager/contact.rb +64 -1
  34. data/app/models/mail_manager/mailable.rb +14 -0
  35. data/app/models/mail_manager/mailer.rb +48 -81
  36. data/app/models/mail_manager/mailing.rb +23 -42
  37. data/app/models/mail_manager/message.rb +52 -8
  38. data/app/models/mail_manager/subscription.rb +9 -3
  39. data/app/models/status_history.rb +3 -2
  40. data/app/views/layouts/mail_manager/application.html.erb +33 -5
  41. data/app/views/layouts/mail_manager/layout.html.erb +15 -0
  42. data/app/views/mail_manager/bounces/index.html.erb +6 -4
  43. data/app/views/mail_manager/bounces/show.html.erb +3 -3
  44. data/app/views/mail_manager/contacts/_form.html.erb +7 -23
  45. data/app/views/mail_manager/contacts/edit.html.erb +3 -3
  46. data/app/views/mail_manager/contacts/index.html.erb +14 -28
  47. data/app/views/mail_manager/contacts/new.html.erb +2 -2
  48. data/app/views/mail_manager/contacts/show.html.erb +5 -5
  49. data/app/views/mail_manager/contacts/subscribe.html.erb +1 -1
  50. data/app/views/mail_manager/mailer/double_opt_in.erb +1 -1
  51. data/app/views/mail_manager/mailer/double_opt_in.html.erb +6 -0
  52. data/app/views/mail_manager/mailer/unsubscribed.erb +1 -1
  53. data/app/views/mail_manager/mailer/unsubscribed.html.erb +1 -1
  54. data/app/views/mail_manager/mailing_lists/_form.html.erb +8 -17
  55. data/app/views/mail_manager/mailing_lists/edit.html.erb +4 -4
  56. data/app/views/mail_manager/mailing_lists/index.html.erb +6 -5
  57. data/app/views/mail_manager/mailing_lists/new.html.erb +3 -3
  58. data/app/views/mail_manager/mailings/_form.html.erb +22 -44
  59. data/app/views/mail_manager/mailings/edit.html.erb +3 -3
  60. data/app/views/mail_manager/mailings/index.html.erb +23 -27
  61. data/app/views/mail_manager/mailings/new.html.erb +2 -2
  62. data/app/views/mail_manager/mailings/test.html.erb +3 -3
  63. data/app/views/mail_manager/messages/index.html.erb +2 -2
  64. data/app/views/mail_manager/subscriptions/_form.html.erb +1 -1
  65. data/app/views/mail_manager/subscriptions/_subscriptions.html.erb +2 -2
  66. data/app/views/mail_manager/subscriptions/edit.html.erb +2 -2
  67. data/app/views/mail_manager/subscriptions/index.html.erb +3 -3
  68. data/app/views/mail_manager/subscriptions/new.html.erb +1 -1
  69. data/app/views/mail_manager/subscriptions/unsubscribe.html.erb +1 -1
  70. data/app/views/mail_manager/subscriptions/unsubscribe_by_email_address.html.erb +3 -3
  71. data/config/locales/en.yml +13 -0
  72. data/config/locales/mailings.en.yml +52 -0
  73. data/config/routes.rb +21 -19
  74. data/db/migrate/008_add_bounces_count_to_mailings.rb +14 -0
  75. data/db/migrate/009_add_messages_count_to_mailings.rb +14 -0
  76. data/db/migrate/010_add_login_token_to_contact.rb +11 -0
  77. data/db/migrate/011_add_deleted_at_to_mailing.rb +11 -0
  78. data/lib/delayed/mailer.rb +9 -5
  79. data/lib/delayed/status.rb +6 -2
  80. data/lib/delayed_overrides/worker.rb +21 -0
  81. data/lib/deleteable.rb +13 -14
  82. data/lib/mail_manager/config.rb +3 -3
  83. data/lib/mail_manager/engine.rb +136 -7
  84. data/lib/mail_manager/lock.rb +1 -0
  85. data/lib/mail_manager/version.rb +1 -1
  86. data/lib/tasks/mail_manager.rake +92 -56
  87. data/mail_manager.gemspec +4 -0
  88. data/spec/rails_helper.rb +50 -0
  89. data/spec/spec_helper.rb +87 -48
  90. data/spec/test_app/.env.development +3 -0
  91. data/spec/test_app/.env.test +2 -0
  92. data/spec/test_app/.rspec +1 -0
  93. data/spec/test_app/Procfile +3 -0
  94. data/spec/test_app/app/controllers/application_controller.rb +4 -0
  95. data/spec/test_app/app/models/ability.rb +7 -0
  96. data/spec/test_app/app/models/user.rb +8 -2
  97. data/spec/test_app/app/models/user_with_role.rb +22 -0
  98. data/spec/test_app/config/database.postgres.yml +21 -0
  99. data/spec/test_app/config/database.sqlite.yml +2 -2
  100. data/spec/test_app/config/environment.rb +2 -2
  101. data/spec/test_app/config/environments/test.rb +13 -0
  102. data/spec/test_app/config/mail_manager.yml +66 -2
  103. data/spec/test_app/config/routes.rb +0 -1
  104. data/spec/test_app/db/migrate/20150420163235_add_bounces_count_to_mailings.rb +14 -0
  105. data/spec/test_app/db/migrate/20150420163804_add_messages_count_to_mailings.rb +14 -0
  106. data/spec/test_app/db/migrate/20150421151457_add_login_token_to_contact.rb +11 -0
  107. data/spec/test_app/db/migrate/20150423143754_add_deleted_at_to_mailing.rb +11 -0
  108. data/spec/test_app/db/schema.rb +10 -5
  109. data/spec/test_app/db/structure.sql +150 -15
  110. data/spec/test_app/features/bounce_management.feature +11 -0
  111. data/spec/test_app/features/contact_management.feature +91 -0
  112. data/{features → spec/test_app/features}/mailable.feature +3 -1
  113. data/spec/test_app/features/mailing_list_management.feature +39 -0
  114. data/spec/test_app/features/mailing_management.feature +60 -0
  115. data/{features → spec/test_app/features}/message.feature +4 -4
  116. data/spec/test_app/features/message_management.feature +22 -0
  117. data/spec/test_app/features/step_definitions/bounce_steps.rb +4 -0
  118. data/spec/test_app/features/step_definitions/contact_steps.rb +63 -0
  119. data/spec/test_app/features/step_definitions/debugging_steps.rb +3 -0
  120. data/spec/test_app/features/step_definitions/email_steps.rb +6 -0
  121. data/spec/test_app/features/step_definitions/job_steps.rb +25 -0
  122. data/spec/test_app/features/step_definitions/login_steps.rb +4 -0
  123. data/spec/test_app/features/step_definitions/mailing_list.rb +17 -0
  124. data/spec/test_app/features/step_definitions/mailing_steps.rb +51 -0
  125. data/spec/test_app/features/step_definitions/subscription_steps.rb +26 -0
  126. data/{features → spec/test_app/features}/step_definitions/webrat_steps.rb +10 -6
  127. data/spec/test_app/features/subscription_management.feature +62 -0
  128. data/spec/test_app/features/support/env.rb +37 -0
  129. data/spec/test_app/features/support/paths.rb +36 -0
  130. data/spec/test_app/lib/debugging.rb +61 -0
  131. data/spec/test_app/lib/post_office_manager.rb +71 -0
  132. data/spec/test_app/public/subscribe.html +40 -0
  133. data/spec/test_app/script/full_suite +50 -0
  134. data/spec/test_app/script/post_office +25 -0
  135. data/spec/test_app/script/rails +20 -0
  136. data/spec/test_app/script/rspec_multi_db +34 -0
  137. data/spec/test_app/spec/controllers/mail_manager/bounces_controller_spec.rb +59 -0
  138. data/spec/test_app/spec/controllers/mail_manager/contacts_controller_spec.rb +178 -0
  139. data/spec/test_app/spec/controllers/mail_manager/mailing_lists_controller_spec.rb +164 -0
  140. data/spec/test_app/spec/controllers/mail_manager/mailings_controller_spec.rb +184 -0
  141. data/spec/test_app/spec/controllers/users_controller_spec.rb +47 -46
  142. data/spec/test_app/spec/factories/_functions.rb +27 -0
  143. data/spec/test_app/spec/factories/contacts.rb +7 -0
  144. data/spec/test_app/spec/factories/mail_manager_bounces.rb +13 -0
  145. data/spec/test_app/spec/factories/mailable.rb +8 -0
  146. data/spec/test_app/spec/factories/mailings.rb +7 -1
  147. data/spec/test_app/spec/factories/message.rb +7 -0
  148. data/spec/test_app/spec/factories/users.rb +19 -7
  149. data/spec/test_app/spec/features/mail_manager/bounce_spec.rb +73 -0
  150. data/spec/test_app/spec/features/mail_manager/double_opt_in_spec.rb +62 -0
  151. data/spec/test_app/spec/features/mail_manager/mailing_spec.rb +46 -0
  152. data/spec/test_app/spec/features/navigation_spec.rb +9 -0
  153. data/spec/test_app/spec/helpers/mail_manager/layout_helper_spec.rb +41 -0
  154. data/spec/test_app/spec/helpers/mail_manager/subscriptions_helper_spec.rb +14 -0
  155. data/spec/test_app/spec/models/delayed/mailer_spec.rb +27 -0
  156. data/spec/test_app/spec/models/delayed/status_job_spec.rb +13 -0
  157. data/spec/test_app/spec/models/delayed/status_spec.rb +37 -0
  158. data/spec/test_app/spec/models/mail_manager/bounce_spec.rb +23 -3
  159. data/spec/test_app/spec/models/mail_manager/engine_spec.rb +79 -0
  160. data/spec/test_app/spec/models/mail_manager/mailable_spec.rb +10 -0
  161. data/spec/test_app/spec/models/mail_manager/mailer_spec.rb +35 -3
  162. data/spec/test_app/spec/models/mail_manager/mailing_list_spec.rb +5 -5
  163. data/spec/test_app/spec/models/mail_manager/mailing_spec.rb +58 -0
  164. data/spec/test_app/spec/models/mail_manager/message_spec.rb +112 -0
  165. data/spec/test_app/spec/models/user_spec.rb +10 -8
  166. data/spec/test_app/spec/rails_helper.rb +86 -0
  167. data/spec/test_app/spec/requests/users_spec.rb +3 -3
  168. data/spec/test_app/spec/routing/mail_manager/bounces_routing_spec.rb +27 -0
  169. data/spec/test_app/spec/routing/mail_manager/contacts_routing_spec.rb +36 -0
  170. data/spec/test_app/spec/routing/mail_manager/mailing_lists_routing_spec.rb +36 -0
  171. data/spec/test_app/spec/routing/mail_manager/mailings_routing_spec.rb +36 -0
  172. data/spec/test_app/spec/spec_helper.rb +82 -32
  173. data/spec/test_app/spec/support/continuance.rb +18 -0
  174. data/spec/test_app/spec/support/custom_matchers.rb +17 -0
  175. data/spec/test_app/spec/support/database_cleaner.rb +10 -1
  176. data/spec/test_app/spec/views/mail_manager/bounces/index.html.erb_spec.rb +32 -0
  177. data/spec/test_app/spec/views/mail_manager/bounces/show.html.erb_spec.rb +12 -0
  178. data/spec/test_app/spec/views/users/edit.html.erb_spec.rb +8 -5
  179. data/spec/test_app/spec/views/users/index.html.erb_spec.rb +10 -19
  180. data/spec/test_app/spec/views/users/new.html.erb_spec.rb +9 -6
  181. data/spec/test_app/spec/views/users/show.html.erb_spec.rb +8 -9
  182. metadata +231 -75
  183. data/.DS_Store +0 -0
  184. data/README.rdoc +0 -3
  185. data/app/.DS_Store +0 -0
  186. data/app/controllers/mail_manager/base_controller.rb +0 -22
  187. data/app/models/.DS_Store +0 -0
  188. data/features/contact_management.feature +0 -24
  189. data/features/mailing_management.feature +0 -78
  190. data/features/step_definitions/email_steps.rb +0 -50
  191. data/features/step_definitions/mlm_steps.rb +0 -11
  192. data/features/step_definitions/pickle_steps.rb +0 -41
  193. data/features/subscription_management.feature +0 -17
  194. data/features/support/env.rb +0 -11
  195. data/features/support/paths.rb +0 -44
  196. data/lib/tasks/mail_manager_tasks.rake +0 -4
  197. data/lib/tasks/rspec.rake +0 -165
  198. data/spec/test_app/bin/cucumber +0 -7
  199. data/spec/test_app/bin/rails +0 -10
  200. data/spec/test_app/bin/rake +0 -7
  201. data/spec/test_app/bin/rspec +0 -7
  202. data/spec/test_app/bin/spring +0 -18
  203. data/spec/test_app/spec/routing/users_routing_spec.rb +0 -35
  204. data/spec/test_app/spec/support/post_office.rb +0 -13
@@ -1,7 +1,5 @@
1
1
  module MailManager
2
- class MailingListsController < BaseController
3
- before_filter :find_mailing_list, :except => [:new,:create,:index]
4
-
2
+ class MailingListsController < MailManager::ApplicationController
5
3
  def index
6
4
  @mailing_lists = MailingList.active.order("name asc").paginate(:page => params[:page])
7
5
  end
@@ -19,7 +17,7 @@ module MailManager
19
17
  def create
20
18
  @mailing_list = MailingList.new(params[:mailing_list])
21
19
  if @mailing_list.save
22
- flash[:notice] = 'MailingList was successfully created.'
20
+ flash[:notice] = 'Mailing List was successfully created.'
23
21
  redirect_to(mail_manager.mailing_lists_path)
24
22
  else
25
23
  render :action => "new"
@@ -28,7 +26,7 @@ module MailManager
28
26
 
29
27
  def update
30
28
  if @mailing_list.update_attributes(params[:mailing_list])
31
- flash[:notice] = 'MailingList was successfully updated.'
29
+ flash[:notice] = 'Mailing List was successfully updated.'
32
30
  redirect_to(mail_manager.mailing_lists_path)
33
31
  else
34
32
  render :action => "edit"
@@ -37,13 +35,8 @@ module MailManager
37
35
 
38
36
  def destroy
39
37
  @mailing_list.destroy
38
+ flash[:notice] = "Mailing List was deleted."
40
39
  redirect_to(mail_manager.mailing_lists_url)
41
40
  end
42
-
43
- protected
44
-
45
- def find_mailing_list
46
- @mailing_list = MailingList.find(params[:id])
47
- end
48
41
  end
49
- end
42
+ end
@@ -1,12 +1,15 @@
1
1
  module MailManager
2
- class MailingsController < BaseController
2
+ class MailingsController < ::MailManager::ApplicationController
3
3
  before_filter :find_mailing, :except => [:new,:create,:index]
4
4
  before_filter :find_all_mailing_lists, :only => [:new,:create,:edit,:update]
5
5
  before_filter :find_mailables, :only => [:new,:create,:edit,:update]
6
6
  before_filter :get_mailables_for_select, :only => [:new,:create,:edit,:update]
7
7
 
8
+ include DeleteableActions
9
+
8
10
  def index
9
- @mailings = Mailing.all.sort_by{|mailing| mailing.created_at}.reverse
11
+ @mailings = Mailing.order("created_at desc").paginate(page: (params[:page] || 1),
12
+ per_page: (params[:per_page] || 10))
10
13
  end
11
14
 
12
15
  def show
@@ -27,22 +30,24 @@ module MailManager
27
30
  end
28
31
 
29
32
  def schedule
30
- @mailing.schedule
33
+ if @mailing.can_schedule?
34
+ @mailing.schedule
35
+ flash[:info] = "Mailing scheduled."
36
+ else
37
+ flash[:warning] = ""
38
+ if @mailing.scheduled_at.nil?
39
+ flash[:warning] += "Error! You must edit your mailing and set a time for your mailing to run.<br/>"
40
+ end
41
+ if @mailing.status != 'pending'
42
+ flash[:warning] += "Error! Your mailing must be pending in order to schedule it."
43
+ end
44
+ end
31
45
  redirect_to mail_manager.mailings_path
32
46
  end
33
47
 
34
48
  def cancel
35
49
  @mailing.cancel
36
- redirect_to mail_manager.mailings_path
37
- end
38
-
39
- def resume
40
- @mailing.resume
41
- redirect_to mail_manager.mailings_path
42
- end
43
-
44
- def pause
45
- @mailing.pause
50
+ flash[:notice] = "Mailing cancelled."
46
51
  redirect_to mail_manager.mailings_path
47
52
  end
48
53
 
@@ -72,11 +77,6 @@ module MailManager
72
77
  end
73
78
  end
74
79
 
75
- def destroy
76
- @mailing.destroy
77
- redirect_to(mail_manager.mailings_url)
78
- end
79
-
80
80
  protected
81
81
 
82
82
  def find_mailing
@@ -1,14 +1,11 @@
1
1
  module MailManager
2
- class MessagesController < ApplicationController
3
- layout 'admin'
4
- before_filter :find_message, :except => [:new,:create,:index]
2
+ class MessagesController < ::MailManager::ApplicationController
5
3
  before_filter :find_mailing
6
4
 
7
5
  def index
8
6
  params[:message] = Hash.new unless params[:message]
9
- params[:message][:status] = 'failed' if params[:message][:status].nil?
10
7
  search_params = params[:message].merge(:mailing_id => params[:mailing_id])
11
- @valid_statuses = Message.valid_statuses
8
+ @valid_statuses = [['Any Status','']] + Message.valid_statuses.map{|s| [s.capitalize,s]}
12
9
  @messages = Message.search(search_params).paginate(:page => params[:page])
13
10
  end
14
11
 
@@ -17,14 +14,10 @@ module MailManager
17
14
 
18
15
  protected
19
16
 
20
- def find_message
21
- @message = Message.find(params[:id])
22
- end
23
-
24
17
  def find_mailing
25
18
  return @mailing = Mailing.find_by_id(params[:mailing_id]) if params[:mailing_id]
26
19
  return @mailing = @message.message.try(:mailing) if @message
27
20
  nil
28
21
  end
29
22
  end
30
- end
23
+ end
@@ -1,22 +1,15 @@
1
1
  module MailManager
2
- class SubscriptionsController < BaseController
3
- before_filter :find_subscription, :except => [:new,:create,:index,:unsubscribe,:unsubscribe_by_email_address]
4
- before_filter :find_mailing_list
5
- before_filter :find_contact, :except => [:new,:create,:index,:unsubscribe,:unsubscribe_by_email_address]
6
- skip_before_filter :authorize, :only => [:unsubscribe,:unsubscribe_by_email_address]
2
+ class SubscriptionsController < ::MailManager::ApplicationController
3
+ skip_before_filter :authorize_resource
4
+ skip_before_filter :load_resource
7
5
 
8
- def index
9
- params[:search] = Hash.new unless params[:search]
10
- search_params = params[:search].merge(:mailing_list_id => params[:mailing_list_id])
11
- @valid_statuses = Subscription.valid_statuses
12
- @subscriptions = Subscription.search(search_params).paginate(:all, :page => params[:page])
13
- end
14
-
6
+ # unsubscribes an email adress by a message's guid(sent in the link)
15
7
  def unsubscribe
16
- raise "Empty id for#{params[:guid]}" if params[:guid].blank?
8
+ raise "Empty id for #{params[:guid]}" if params[:guid].blank?
17
9
  if params[:guid] =~ /^test/
18
10
  @message = TestMessage.find_by_guid(params[:guid])
19
11
  @mailing_lists = ['Test Mailing List']
12
+ @email_address = @message.test_email_address
20
13
  @contact = Contact.new(:first_name => 'Test', :last_name => 'Guy',
21
14
  :email_address => @message.test_email_address)
22
15
  else
@@ -25,80 +18,28 @@ module MailManager
25
18
  subscription.mailing_list.nil?}.collect{|subscription| subscription.mailing_list.name}
26
19
  @contact = Message.find_by_guid(params[:guid]).try(:contact)
27
20
  raise "Could not find your subscription. Please try unsubscribing with your email address." if @contact.nil?
21
+ @email_address = @contact.email_address
28
22
  end
29
- render 'unsubscribe', :layout => 'layout'
23
+ render 'unsubscribe', :layout => MailManager.public_layout
30
24
  rescue => e
25
+ # :nocov: catastrophic failure... shouldn't happen
31
26
  Rails.logger.warn "Error unsubscribing: #{e.message}\n #{e.backtrace.join("\n ")}"
32
- flash[:error] = e.message
33
- redirect_to mail_manager.unsubscribe_by_email_address_path
27
+ flash[:error] = "We did not recognize that unsubscribe url! Please try unsubscribing with your email address."
28
+ redirect_to main_app.unsubscribe_by_email_address_path
29
+ # :nocov:
34
30
  end
35
31
 
32
+ # prints/executes form for unsubscribing by email address
36
33
  def unsubscribe_by_email_address
37
34
  unless params[:email_address].blank?
38
35
  unsubscribed_subscriptions = Subscription.unsubscribe_by_email_address(params[:email_address])
36
+ @email_address = params[:email_address]
39
37
  @mailing_lists = unsubscribed_subscriptions.reject{|subscription|
40
38
  subscription.mailing_list.nil?}.collect{|subscription| subscription.mailing_list.name}
41
39
  @contact = Contact.new(:email_address => params[:email_address])
42
- return render('unsubscribe', :layout => 'layout')
43
- end
44
- render :layout => 'layout'
45
- end
46
-
47
- def show
48
- end
49
-
50
- def new
51
- @subscription = Subscription.new
52
- @subscription.mailing_list = @mailing_list
53
- @contact = @subscription
54
- end
55
-
56
- def edit
57
- end
58
-
59
- def create
60
- @subscription = Subscription.new(params[:subscription])
61
- @subscription.mailing_list_id = @mailing_list.id
62
- if @subscription.save
63
- flash[:notice] = 'Subscription was successfully created.'
64
- return redirect_to(mail_manager.mailing_list_subscriptions_path(@mailing_list))
65
- else
66
- @contact = @subscription
67
- render :action => "new"
40
+ return render('unsubscribe', :layout => MailManager.public_layout)
68
41
  end
69
- end
70
-
71
- def update
72
- if @subscription.update_attributes(params[:subscription])
73
- @subscription.change_status(params[:subscription][:status])
74
- flash[:notice] = 'Subscription was successfully updated.'
75
- redirect_to(mail_manager.mailing_list_subscriptions_path(@mailing_list))
76
- else
77
- render :action => "edit"
78
- end
79
- end
80
-
81
- def destroy
82
- @subscription.destroy
83
- redirect_to(mail_manager.subscriptions_url)
84
- end
85
-
86
- protected
87
-
88
- def find_subscription
89
- @subscription = Subscription.find(params[:id])
90
- end
91
-
92
- def find_mailing_list
93
- return @mailing_list = @subscription.mailing_list if @subscription
94
- return @mailing_list = MailingList.find_by_id(params[:mailing_list_id]) if params[:mailing_list_id]
95
- return @mailing_list = MailingList.find_by_id(params[:subscription][:mailing_list_id]) if
96
- params[:subscription]
97
- nil
98
- end
99
-
100
- def find_contact
101
- @contact = @subscription.contact
42
+ render :layout => MailManager.public_layout
102
43
  end
103
44
  end
104
45
  end
@@ -0,0 +1,43 @@
1
+ module MailManager
2
+ module LayoutHelper
3
+ def title(value=nil, locals={})
4
+ if value.nil?
5
+ @page_title
6
+ else
7
+ @page_title = translate( value, locals)
8
+ "<h1>#{@page_title}</h1>".html_safe
9
+ end
10
+ end
11
+
12
+ def use_show_for_resources?
13
+ ::MailManager.use_show_for_resources
14
+ rescue
15
+ # :nocov: shouldn't happen
16
+ false
17
+ # :nocov:
18
+ end
19
+
20
+ def show_title?
21
+ return @show_title if defined? @show_title
22
+ true
23
+ rescue
24
+ # :nocov: shouldn't happen
25
+ false
26
+ # :nocov:
27
+ end
28
+
29
+ def site_url
30
+ ::MailManager.site_url
31
+ rescue
32
+ # :nocov: shouldn't happen
33
+ "#{default_url_options[:protocol]||'http'}://#{default_url_options[:domain]}"
34
+ # :nocov:
35
+ end
36
+
37
+ def translate(key, options={})
38
+ super(key, options.merge(raise: true))
39
+ rescue I18n::MissingTranslationData
40
+ key
41
+ end
42
+ end
43
+ end
@@ -20,19 +20,24 @@ module MailManager
20
20
  class Bounce < ActiveRecord::Base
21
21
  self.table_name = "#{MailManager.table_prefix}bounces"
22
22
  belongs_to :message, :class_name => 'MailManager::Message'
23
- belongs_to :mailing, :class_name => 'MailManager::Mailing'
23
+ belongs_to :mailing, :class_name => 'MailManager::Mailing', counter_cache: true
24
24
  include StatusHistory
25
25
  override_statuses(['needs_manual_intervention','unprocessed','dismissed','resolved','invalid'],'unprocessed')
26
26
  before_create :set_default_status
27
- default_scope :order => "#{MailManager.table_prefix}contacts.last_name, #{MailManager.table_prefix}contacts.first_name, #{MailManager.table_prefix}contacts.email_address",
28
- :joins =>
29
- "LEFT OUTER JOIN #{MailManager.table_prefix}messages on #{MailManager.table_prefix}bounces.message_id=#{MailManager.table_prefix}messages.id "+
30
- " LEFT OUTER JOIN #{MailManager.table_prefix}contacts on #{MailManager.table_prefix}messages.contact_id=#{MailManager.table_prefix}contacts.id"
27
+ #default_scope :order => "#{MailManager.table_prefix}contacts.last_name, #{MailManager.table_prefix}contacts.first_name, #{MailManager.table_prefix}contacts.email_address",
28
+ # :joins =>
29
+ # "LEFT OUTER JOIN #{MailManager.table_prefix}messages on #{MailManager.table_prefix}bounces.message_id=#{MailManager.table_prefix}messages.id "+
30
+ # " LEFT OUTER JOIN #{MailManager.table_prefix}contacts on #{MailManager.table_prefix}messages.contact_id=#{MailManager.table_prefix}contacts.id"
31
31
  #
32
32
  scope :by_mailing_id, lambda {|mailing_id| where(:mailing_id => mailing_id)}
33
33
  scope :by_status, lambda {|status| where(:status => status.to_s)}
34
34
 
35
+ before_save :fix_counter_cache, if: lambda {|bounce| !bounce.new_record? &&
36
+ bounce.mailing_id_changed?
37
+ }
38
+
35
39
  attr_protected :id
40
+
36
41
  #Parses email contents for bounce resolution
37
42
  def process(force=false)
38
43
  if status.eql?('unprocessed') || force
@@ -129,5 +134,11 @@ module MailManager
129
134
  return @email if @email
130
135
  @email = Mail.new(bounce_message)
131
136
  end
137
+
138
+ protected
139
+ def fix_counter_cache
140
+ MailManager::Mailing.decrement_counter(:bounces_count, self.mailing_id_was) if self.mailing_id_was.present?
141
+ MailManager::Mailing.increment_counter(:bounces_count, self.mailing_id)
142
+ end
132
143
  end
133
144
  end
@@ -66,12 +66,65 @@ module MailManager
66
66
  end
67
67
 
68
68
  def self.signup(params)
69
- contact = MailManager::Contact.active.find_by_email_address(params['email_address'])
69
+ contact = Contact.active.find_by_email_address(params['email_address'])
70
70
  contact ||= Contact.new
71
71
  Rails.logger.debug "Updating contact(#{contact.new_record? ? "New" : contact.id}) params: #{params.inspect}"
72
72
  contact.update_attributes(params)
73
73
  contact
74
74
  end
75
+
76
+ def double_opt_in(mailing_list_ids)
77
+ previously_active_subscriptions = subscriptions.select(&:"active?")
78
+ new_subscriptions = subscriptions.select do |s|
79
+ mailing_list_ids.include?(s.mailing_list_id.to_s)
80
+ end
81
+ if new_subscriptions.present?
82
+ new_subscriptions.each{|subscription| subscription.change_status(:pending)}
83
+ self.delay.deliver_double_opt_in
84
+ end
85
+ nil
86
+ end
87
+
88
+ def deliver_double_opt_in
89
+ Mailer.double_opt_in(self).deliver
90
+ end
91
+
92
+ def double_opt_in_url
93
+ "#{MailManager.site_url}#{MailManager.double_opt_in_path}/#{login_token}"
94
+ end
95
+
96
+ def self.inject_contact_id(token,id)
97
+ token = token.split('')
98
+ id_string = id.to_s.split('')
99
+ new_token = ""
100
+ 0.upto(39) do |index|
101
+ new_token << token.pop
102
+ new_token << id_string.shift unless id_string.blank?
103
+ end
104
+ new_token
105
+ end
106
+
107
+ def self.extract_contact_id(token)
108
+ token = token.split('')
109
+ id_string = ""
110
+ 0.upto(token.length - 41) do |index|
111
+ token.shift
112
+ id_string << token.shift
113
+ end
114
+ id_string.to_i
115
+ end
116
+
117
+ def self.find_by_token(token)
118
+ Contact.find_by_id(Contact::extract_contact_id(token))
119
+ end
120
+
121
+ def login_token
122
+ self[:login_token] ||= generate_login_token
123
+ end
124
+
125
+ def authorized?(token)
126
+ login_token.eql?(token) and login_token_created_at > 2.days.ago
127
+ end
75
128
 
76
129
  def initialize_subscriptions
77
130
  @subscriptions = new_record? ? [] : Subscription.find_all_by_contact_id(self.id)
@@ -87,5 +140,15 @@ module MailManager
87
140
  subscription.mailing_list.nil?}.sort_by{|subscription|
88
141
  subscription.mailing_list.name.downcase}
89
142
  end
143
+
144
+ # generated the token for which an opt-in is emailed
145
+ def generate_login_token
146
+ time = Time.now
147
+ token = Contact::inject_contact_id("#{Digest::SHA1.hexdigest(
148
+ "#{self.id}#{::MailManager.secret}#{time}")}", self.id)
149
+ self.update_attribute(:login_token, token)
150
+ self.update_attribute(:login_token_created_at, time)
151
+ token
152
+ end
90
153
  end
91
154
  end
@@ -0,0 +1,14 @@
1
+ class MailManager::Mailable < ActiveRecord::Base
2
+ attr_accessible :name, :email_text, :email_html
3
+ default_scope where(reusable: true)
4
+ include MailManager::MailableRegistry::Mailable
5
+ end
6
+
7
+ MailManager::MailableRegistry.register('MailManager::Mailable',{
8
+ :find_mailables => :all,
9
+ :name => :name,
10
+ :parts => [
11
+ ['text/plain', :email_text],
12
+ ['text/html', :email_html]
13
+ ]
14
+ })
@@ -12,37 +12,50 @@ mail - knows how to send any message based on the different "mime" parts its giv
12
12
 
13
13
  =end
14
14
 
15
- require 'net/http'
16
- require 'uri'
15
+ require 'open-uri'
17
16
  require "base64"
18
17
  begin
19
18
  require "mini_magick"
20
19
  rescue => e
20
+ # :nocov:
21
21
  require 'rmagick' rescue nil
22
+ # :nocov:
22
23
  end
23
24
 
24
25
 
25
26
  module MailManager
26
27
  class Mailer < ActionMailer::Base
27
- def unsubscribed(message,subscriptions)
28
- @contact = message.contact
29
- @recipients = @contact.email_address
30
- @from = message.from_email_address
31
- @message = message
28
+ # send a confirmation email for unsubscribing
29
+ def unsubscribed(subscriptions,email_address,contact=nil,message=nil)
30
+ @contact = contact
31
+ @email_address = email_address
32
+ @recipients = @email_address
33
+ @from = message.try(:from_email_address) || MailManager.default_from_email_address
32
34
  @mailing_lists = subscriptions.reject{|subscription| subscription.mailing_list.nil?}.
33
35
  collect{|subscription| subscription.mailing_list.name}
34
36
  @subject = "Unsubscribed from #{@mailing_lists.join(',')} at #{MailManager.site_url}"
35
- Rails.logger.debug "Really Sending Unsubscribed from #{@mailing_lists.first} to #{@contact.email_address}"
37
+ Rails.logger.debug "Really Sending Unsubscribed from #{@mailing_lists.first} to #{@email_address}"
38
+ mail(to: @recipients, from: @from, subject: @subject)
39
+ end
40
+
41
+ def double_opt_in(contact)
42
+ @contact = contact
43
+ @recipients = @contact.email_address
44
+ @subject = "Confirm Newsletter Subscription at #{::MailManager.site_url}"
45
+ @from = ::MailManager.signup_email_address
46
+ @mailing_list_names = contact.subscriptions.map(&:mailing_list).map(&:name).join(', ')
47
+ @headers = {'Return-Path' => ::MailManager.bounce['email_address']}
36
48
  mail(to: @recipients, from: @from, subject: @subject)
37
49
  end
38
50
 
39
- # we do special junk ... so lets make them class methods
40
51
  class << self
52
+ # send a mailing related to the message's data
41
53
  def deliver_message(message)
42
54
  self.send_mail(message.subject,message.email_address_with_name,message.from_email_address,
43
55
  message.parts,message.guid,message.mailing.include_images?)
44
56
  end
45
57
 
58
+ # create mailing; parsing html sources for images to attach/include
46
59
  def multipart_with_inline_images(subject,to_email_address,from_email_address,the_parts,message_id=nil,include_images=true)
47
60
  text_source = the_parts.first[1];nil
48
61
  original_html_source = the_parts.last[1];nil
@@ -72,6 +85,7 @@ module MailManager
72
85
  mail
73
86
  end
74
87
 
88
+ # create mailing without fetching image data
75
89
  def multipart_alternative_without_images(subject,to_email_address,from_email_address,the_parts,message_id=nil,include_images=true)
76
90
  text_source = the_parts.first[1];nil
77
91
  original_html_source = the_parts.last[1];nil
@@ -92,6 +106,7 @@ module MailManager
92
106
  mail
93
107
  end
94
108
 
109
+ # send the mailing with the given subject, addresses, and parts
95
110
  def send_mail(subject,to_email_address,from_email_address,the_parts,message_id=nil,include_images=true)
96
111
  include_images = (include_images and !MailManager.dont_include_images_domains.detect{|domain|
97
112
  to_email_address.strip =~ /#{domain}>?$/})
@@ -108,8 +123,11 @@ module MailManager
108
123
  Rails.logger.debug mail.to_s
109
124
  end
110
125
 
126
+ # set mail delivery settings
111
127
  def set_mail_settings(mail)
112
- mail.delivery_method ActionMailer::Base.delivery_method.eql?(:letter_opener) ? :test : ActionMailer::Base.delivery_method
128
+ delivery_method = ActionMailer::Base.delivery_method
129
+ delivery_method = delivery_method.eql?(:letter_opener) ? :test : delivery_method
130
+ mail.delivery_method delivery_method
113
131
  # letter opener blows up!
114
132
  # Ex set options!
115
133
  # mail.delivery_method.settings.merge!( {
@@ -123,25 +141,20 @@ module MailManager
123
141
  # } )
124
142
 
125
143
  mail.delivery_method.settings.merge!(
126
- (case method
144
+ (case delivery_method
127
145
  when :smtp then ActionMailer::Base.smtp_settings
146
+ # :nocov:
128
147
  when :sendmail then ActionMailer::Base.sendmail_settings
148
+ # :nocov:
129
149
  else
130
150
  {}
131
151
  end rescue {})
132
152
  )
133
153
  end
134
154
 
135
- def inline_attachment(params, &block)
136
- params = { :content_type => params } if String === params
137
- params = { :disposition => "inline",
138
- :transfer_encoding => "base64" }.merge(params)
139
- params[:headers] ||= {}
140
- params[:headers]['Content-ID'] = params[:cid]
141
- params
142
- end
143
-
155
+ # return mime type for images by extension
144
156
  def image_mime_types(extension)
157
+ # :nocov:
145
158
  case extension.downcase
146
159
  when 'bmp' then 'image/bmp'
147
160
  when 'cod' then 'image/cis-cod'
@@ -156,13 +169,21 @@ module MailManager
156
169
  when 'tif' then 'image/tiff'
157
170
  when 'tiff' then 'image/tiff'
158
171
  end
172
+ # :nocov:
159
173
  end
160
174
 
175
+ # find the extension for images by inspecting their data
161
176
  def get_extension_from_data(image_data)
162
177
  if defined?(MiniMagick)
163
- MiniMagick::Image.read(image_data)[:format] || ''
178
+ format = ''
179
+ file = Tempfile.new('get-extension','tmp')
180
+ file.close
181
+ File.open(file.path,'wb'){|binfile| binfile.write(image_data)}
182
+ MiniMagick::Image.open(file.path)[:format] || ''
164
183
  elsif defined?(Magick)
184
+ # :nocov: currently on ly mini_magick is tested
165
185
  Magick::Image.from_blob(image_data).first.format || ''
186
+ # :nocov:
166
187
  else
167
188
  ''
168
189
  end
@@ -170,6 +191,8 @@ module MailManager
170
191
  ''
171
192
  end
172
193
 
194
+
195
+ # parses html and retrieves images and inserts them with CID/attachments
173
196
  def inline_html_with_images(html_source)
174
197
  parsed_data = html_source.split(/(<\s*img[^>]+src\s*=\s*["'])([^"']*)(["'])/i)
175
198
  images = Array.new
@@ -200,72 +223,16 @@ module MailManager
200
223
  end
201
224
  raise image_errors unless image_errors.eql?('')
202
225
  [final_html,images]
203
- # related_part = Mail::Part.new do
204
- # body final_html
205
- # end
206
- # images.each do |image|
207
- # related_part.part inline_attachment(image)
208
- # end
209
- # related_part.content_type = 'multipart/related'
210
- # related_part
211
-
212
- # related_part = Mail::Part.new do
213
- # content_type 'multipart/related'
214
- # # content_type 'text/html; charset=UTF-8'
215
- # # body final_html
216
- # end
217
- # related_part.parts << Mail::Part.new do
218
- # content_type 'text/html; charset=UTF-8'
219
- # body final_html
220
- # end
221
- # images.each do |image|
222
- # related_part.attachments[image[:filename]] = image[:body]
223
- # end
224
- # related_part.content_type = 'multipart/related'
225
- # related_part.parts.first.content_type = 'text/html; charset=UTF-8'
226
- # related_part.parts.first.header['Content-Disposition'] = 'inline'
227
-
228
- end
229
-
230
- def local_ips
231
- `/sbin/ifconfig`
232
226
  end
233
227
 
234
- def request_local?(uri_str)
235
- uri = URI.parse(uri_str)
236
- ip_address = `host #{uri.host}`.gsub(/.*has address ([\d\.]+)\s.*/m,"\\1")
237
- local_ips.include?(ip_address)
238
- rescue => e
239
- false
240
- end
241
-
228
+ # fetch the data from a url (used for images)
242
229
  def fetch(uri_str, limit = 10)
243
- # You should choose better exception.
244
- raise ArgumentError, 'HTTP redirect too deep' if limit == 0
245
230
  uri = URI.parse(uri_str)
246
- request = Net::HTTP::Get.new("#{uri.path}#{"?"+uri.query if uri.query.to_s.strip != ''}")
247
-
248
- response = Net::HTTP.start(
249
- uri.host, uri.port,
250
- :use_ssl => uri.scheme == 'https',
251
- :verify_mode => OpenSSL::SSL::VERIFY_NONE) do |https|
252
- https.request(request)
253
- end
254
- case response
255
- when Net::HTTPSuccess then response.body
256
- when Net::HTTPRedirection then fetch(response['location'], limit - 1)
231
+ if uri.scheme.eql?('file')
232
+ File.binread(uri_str.gsub(%r#^file://#,''))
257
233
  else
258
- response.error!
234
+ uri.read
259
235
  end
260
- # CURB version - gem wouldn't install anymore on CentOS
261
- # body = ''
262
- # Curl.get(uri_str) do |http|
263
- # http.follow_location = true
264
- # http.interface = '127.0.0.1' if request_local?(uri_str)
265
- # http.on_success{|response| body = response.body}
266
- # end
267
- # raise Exception.new("Couldn't fetch URL: #{uri_str}") unless body.present?
268
- # body
269
236
  end
270
237
  end
271
238
  end