mlist 0.1.11 → 0.1.12
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.
- data/README +2 -1
- data/VERSION.yml +1 -1
- data/lib/mlist/mail_list.rb +64 -46
- data/lib/mlist/util/email_helpers.rb +56 -20
- data/lib/mlist/util/quoting.rb +1 -3
- data/lib/mlist/util/tmail_methods.rb +36 -24
- data/spec/models/mail_list_spec.rb +98 -71
- metadata +12 -5
data/README
CHANGED
@@ -141,7 +141,7 @@ environment.rb after the initialize block (our gem needs to have been loaded):
|
|
141
141
|
# Please do specify a version, and check for the latest!
|
142
142
|
config.gem "mlist", :version => '0.1.0'
|
143
143
|
end
|
144
|
-
|
144
|
+
|
145
145
|
MLIST_SERVER = MList::Server.new(
|
146
146
|
:list_manager => MList::Manager::Database.new,
|
147
147
|
:email_server => MList::EmailServer::Default.new(
|
@@ -185,6 +185,7 @@ list something like this:
|
|
185
185
|
(The MIT License)
|
186
186
|
|
187
187
|
Copyright (c) 2008-2009 Adam Williams (aiwilliams)
|
188
|
+
Portions from Rails are Copyright (c) 2004-2008 David Heinemeier Hansson
|
188
189
|
|
189
190
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
190
191
|
this software and associated documentation files (the 'Software'), to deal in the
|
data/VERSION.yml
CHANGED
data/lib/mlist/mail_list.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
module MList
|
2
2
|
class MailList < ActiveRecord::Base
|
3
3
|
set_table_name 'mlist_mail_lists'
|
4
|
-
|
4
|
+
|
5
5
|
# Provides the MailList for a given implementation of MList::List,
|
6
6
|
# connecting it to the provided email server for delivering posts.
|
7
7
|
#
|
@@ -17,19 +17,19 @@ module MList
|
|
17
17
|
mail_list.outgoing_server = outgoing_server
|
18
18
|
mail_list
|
19
19
|
end
|
20
|
-
|
20
|
+
|
21
21
|
include MList::Util::EmailHelpers
|
22
|
-
|
22
|
+
|
23
23
|
belongs_to :manager_list, :polymorphic => true
|
24
|
-
|
24
|
+
|
25
25
|
before_destroy :delete_unreferenced_email
|
26
26
|
has_many :messages, :class_name => 'MList::Message', :dependent => :delete_all
|
27
27
|
has_many :threads, :class_name => 'MList::Thread', :dependent => :delete_all
|
28
|
-
|
28
|
+
|
29
29
|
delegate :address, :label, :post_url, :to => :list
|
30
|
-
|
30
|
+
|
31
31
|
attr_accessor :outgoing_server
|
32
|
-
|
32
|
+
|
33
33
|
# Creates a new MList::Message and delivers it to the subscribers of this
|
34
34
|
# list.
|
35
35
|
#
|
@@ -45,7 +45,7 @@ module MList
|
|
45
45
|
:email => MList::Email.new(:source => email.to_s)
|
46
46
|
), :search_parent => false, :copy_sender => email.copy_sender
|
47
47
|
end
|
48
|
-
|
48
|
+
|
49
49
|
# Processes the email received by the MList::Server.
|
50
50
|
#
|
51
51
|
def process_email(email, subscriber)
|
@@ -57,7 +57,7 @@ module MList
|
|
57
57
|
:email => email
|
58
58
|
), :copy_sender => list.copy_sender?(subscriber)
|
59
59
|
end
|
60
|
-
|
60
|
+
|
61
61
|
# Answers the provided subject with superfluous 're:' and this list's
|
62
62
|
# labels removed.
|
63
63
|
#
|
@@ -72,14 +72,14 @@ module MList
|
|
72
72
|
without_label
|
73
73
|
end
|
74
74
|
end
|
75
|
-
|
75
|
+
|
76
76
|
def find_parent_message(email)
|
77
77
|
if in_reply_to = email.header_string('in-reply-to')
|
78
78
|
message = messages.find(:first,
|
79
79
|
:conditions => ['identifier = ?', remove_brackets(in_reply_to)])
|
80
80
|
return message if message
|
81
81
|
end
|
82
|
-
|
82
|
+
|
83
83
|
if email.references
|
84
84
|
reference_identifiers = email.references.collect {|rid| remove_brackets(rid)}
|
85
85
|
message = messages.find(:first,
|
@@ -87,7 +87,7 @@ module MList
|
|
87
87
|
:order => 'created_at desc')
|
88
88
|
return message if message
|
89
89
|
end
|
90
|
-
|
90
|
+
|
91
91
|
if email.subject =~ REGARD_RE
|
92
92
|
message = messages.find(:first,
|
93
93
|
:conditions => ['subject = ?', remove_regard(clean_subject(email.subject))],
|
@@ -95,13 +95,13 @@ module MList
|
|
95
95
|
return message if message
|
96
96
|
end
|
97
97
|
end
|
98
|
-
|
98
|
+
|
99
99
|
# The MList::List instance of the list manager.
|
100
100
|
#
|
101
101
|
def list
|
102
102
|
@list ||= manager_list
|
103
103
|
end
|
104
|
-
|
104
|
+
|
105
105
|
def manager_list_with_dual_type=(list)
|
106
106
|
if list.is_a?(ActiveRecord::Base)
|
107
107
|
self.manager_list_without_dual_type = list
|
@@ -112,27 +112,27 @@ module MList
|
|
112
112
|
end
|
113
113
|
end
|
114
114
|
alias_method_chain :manager_list=, :dual_type
|
115
|
-
|
115
|
+
|
116
116
|
# Distinct footer start marker. It is important to realize that changing
|
117
117
|
# this could be problematic.
|
118
118
|
#
|
119
119
|
FOOTER_BLOCK_START = "-~----~~----~----~----~----~---~~-~----~------~--~-~-"
|
120
|
-
|
120
|
+
|
121
121
|
# Distinct footer end marker. It is important to realize that changing
|
122
122
|
# this could be problematic.
|
123
123
|
#
|
124
124
|
FOOTER_BLOCK_END = "--~--~---~-----~--~----~-----~~~----~---~---~--~----~"
|
125
|
-
|
125
|
+
|
126
126
|
private
|
127
127
|
FOOTER_BLOCK_START_RE = %r[#{FOOTER_BLOCK_START}]
|
128
128
|
FOOTER_BLOCK_END_RE = %r[#{FOOTER_BLOCK_END}]
|
129
|
-
|
129
|
+
|
130
130
|
# http://mail.python.org/pipermail/mailman-developers/2006-April/018718.html
|
131
131
|
def bounce_headers
|
132
132
|
# tmail would not correctly quote the label in the sender header, which would break smtp delivery
|
133
133
|
{'sender' => "<mlist-#{address}>", 'errors-to' => "#{label} <mlist-#{address}>"}
|
134
134
|
end
|
135
|
-
|
135
|
+
|
136
136
|
def delete_unreferenced_email
|
137
137
|
conditions = %Q{
|
138
138
|
mlist_emails.id in (
|
@@ -144,7 +144,7 @@ module MList
|
|
144
144
|
)}
|
145
145
|
MList::Email.delete_all(conditions)
|
146
146
|
end
|
147
|
-
|
147
|
+
|
148
148
|
def strip_list_footers(content)
|
149
149
|
if content =~ FOOTER_BLOCK_START_RE
|
150
150
|
in_footer_block = false
|
@@ -160,7 +160,7 @@ module MList
|
|
160
160
|
end
|
161
161
|
content
|
162
162
|
end
|
163
|
-
|
163
|
+
|
164
164
|
# http://www.jamesshuggins.com/h/web1/list-email-headers.htm
|
165
165
|
def list_headers
|
166
166
|
headers = list.list_headers.dup
|
@@ -169,43 +169,43 @@ module MList
|
|
169
169
|
headers.update(bounce_headers)
|
170
170
|
headers.delete_if {|k,v| v.nil?}
|
171
171
|
end
|
172
|
-
|
172
|
+
|
173
173
|
# Answer headers values which should be stripped from outgoing email.
|
174
174
|
#
|
175
175
|
def strip_headers
|
176
176
|
%w(return-receipt-to domainkey-signature dkim-signature)
|
177
177
|
end
|
178
|
-
|
178
|
+
|
179
179
|
def process_message(message, options = {})
|
180
180
|
raise MList::DoubleDeliveryError.new(message) unless message.new_record?
|
181
|
-
|
181
|
+
|
182
182
|
options = {
|
183
183
|
:search_parent => true,
|
184
184
|
:delivery_time => Time.now,
|
185
185
|
:copy_sender => false
|
186
186
|
}.merge(options)
|
187
|
-
|
187
|
+
|
188
188
|
transaction do
|
189
189
|
thread = find_thread(message, options)
|
190
190
|
thread.updated_at = options[:delivery_time]
|
191
|
-
|
191
|
+
|
192
192
|
delivery = prepare_delivery(message, options)
|
193
193
|
thread.messages << message
|
194
|
-
|
194
|
+
|
195
195
|
self.updated_at = options[:delivery_time]
|
196
196
|
thread.save! && save!
|
197
|
-
|
197
|
+
|
198
198
|
outgoing_server.deliver(delivery.tmail)
|
199
199
|
end
|
200
|
-
|
200
|
+
|
201
201
|
message
|
202
202
|
end
|
203
|
-
|
203
|
+
|
204
204
|
def prepare_delivery(message, options)
|
205
205
|
message.identifier = outgoing_server.generate_message_id
|
206
206
|
message.created_at = options[:delivery_time]
|
207
207
|
message.subject = clean_subject(message.subject)
|
208
|
-
|
208
|
+
|
209
209
|
recipient_addresses = message.recipient_addresses
|
210
210
|
sender_address = message.subscriber.email_address
|
211
211
|
if options[:copy_sender]
|
@@ -213,7 +213,7 @@ module MList
|
|
213
213
|
else
|
214
214
|
recipient_addresses.delete(sender_address)
|
215
215
|
end
|
216
|
-
|
216
|
+
|
217
217
|
returning(message.delivery) do |delivery|
|
218
218
|
delivery.date ||= options[:delivery_time]
|
219
219
|
delivery.message_id = message.identifier
|
@@ -228,22 +228,40 @@ module MList
|
|
228
228
|
prepare_list_footer(delivery, message)
|
229
229
|
end
|
230
230
|
end
|
231
|
-
|
231
|
+
|
232
232
|
def prepare_list_footer(delivery, message)
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
233
|
+
footer = list_footer(message)
|
234
|
+
prepare_html_footer(delivery.text_html_part, footer)
|
235
|
+
prepare_text_footer(delivery.text_plain_part, footer)
|
236
|
+
end
|
237
|
+
|
238
|
+
def prepare_html_footer(part, footer)
|
239
|
+
return unless part
|
240
|
+
content = part.body
|
241
|
+
content.gsub!(%r(<p>\s*#{FOOTER_BLOCK_START_RE}.*?#{FOOTER_BLOCK_END_RE}\s*<\/p>)im, '')
|
242
|
+
content.strip!
|
243
|
+
html_footer = "<p>#{auto_link_urls(text_to_html(footer))}</p>"
|
244
|
+
|
245
|
+
unless content.sub!(/<\/body>/, "#{html_footer}</body>")
|
246
|
+
# no body was there, substitution failed
|
247
|
+
content << html_footer
|
248
|
+
end
|
249
|
+
part.body = content
|
250
|
+
end
|
251
|
+
|
252
|
+
def prepare_text_footer(part, footer)
|
253
|
+
return unless part
|
254
|
+
content = strip_list_footers(part.body)
|
237
255
|
content << "\n\n" unless content.end_with?("\n\n")
|
238
|
-
content <<
|
239
|
-
|
256
|
+
content << footer
|
257
|
+
part.body = content
|
240
258
|
end
|
241
|
-
|
259
|
+
|
242
260
|
def list_footer(message)
|
243
261
|
content = list.footer_content(message)
|
244
262
|
"#{FOOTER_BLOCK_START}\n#{content}\n#{FOOTER_BLOCK_END}"
|
245
263
|
end
|
246
|
-
|
264
|
+
|
247
265
|
def list_subject(string)
|
248
266
|
list_subject = string.dup
|
249
267
|
if list_subject =~ REGARD_RE
|
@@ -252,12 +270,12 @@ module MList
|
|
252
270
|
"#{subject_prefix} #{list_subject}"
|
253
271
|
end
|
254
272
|
end
|
255
|
-
|
273
|
+
|
256
274
|
def find_thread(message, options)
|
257
275
|
message.parent = find_parent_message(message.email) if message.email && options[:search_parent]
|
258
276
|
message.parent ? message.parent.thread : threads.build
|
259
277
|
end
|
260
|
-
|
278
|
+
|
261
279
|
def reply_to_header(message)
|
262
280
|
if list.reply_to_list?
|
263
281
|
"#{label} #{bracket(address)}"
|
@@ -265,13 +283,13 @@ module MList
|
|
265
283
|
subscriber_name_and_address(message.subscriber)
|
266
284
|
end
|
267
285
|
end
|
268
|
-
|
286
|
+
|
269
287
|
def subject_prefix_regex
|
270
288
|
@subject_prefix_regex ||= Regexp.new(Regexp.escape(subject_prefix) + ' ')
|
271
289
|
end
|
272
|
-
|
290
|
+
|
273
291
|
def subject_prefix
|
274
292
|
@subject_prefix ||= "[#{label}]"
|
275
293
|
end
|
276
294
|
end
|
277
|
-
end
|
295
|
+
end
|
@@ -1,18 +1,18 @@
|
|
1
1
|
module MList
|
2
2
|
module Util
|
3
|
-
|
3
|
+
|
4
4
|
class HtmlTextExtraction
|
5
|
-
|
5
|
+
|
6
6
|
# We need a way to maintain non-breaking spaces. Hpricot will replace
|
7
7
|
# them with ??.chr. We can easily teach it to convert it to a space, but
|
8
8
|
# then we lose the information in the Text node that we need to keep the
|
9
9
|
# space around, since that is what they would see in a view of the HTML.
|
10
10
|
NBSP = '!!!NBSP!!!'
|
11
|
-
|
11
|
+
|
12
12
|
def initialize(html)
|
13
13
|
@doc = Hpricot(html.gsub(' ', NBSP))
|
14
14
|
end
|
15
|
-
|
15
|
+
|
16
16
|
def execute
|
17
17
|
@text, @anchors = '', []
|
18
18
|
@doc.each_child do |node|
|
@@ -28,7 +28,7 @@ module MList
|
|
28
28
|
end
|
29
29
|
@text.gsub(NBSP, ' ')
|
30
30
|
end
|
31
|
-
|
31
|
+
|
32
32
|
def extract_text_from_node(node)
|
33
33
|
case node.name
|
34
34
|
when 'head'
|
@@ -67,7 +67,7 @@ module MList
|
|
67
67
|
extract_text_from_children(node)
|
68
68
|
end
|
69
69
|
end
|
70
|
-
|
70
|
+
|
71
71
|
def extract_text_from_children(elem)
|
72
72
|
elem.each_child do |node|
|
73
73
|
case node
|
@@ -78,45 +78,81 @@ module MList
|
|
78
78
|
end
|
79
79
|
end
|
80
80
|
end
|
81
|
-
|
81
|
+
|
82
82
|
def extract_text_from_text_node(node)
|
83
83
|
text = @text.end_with?("\n") ? node.inner_text.lstrip : node.inner_text
|
84
84
|
@text << text.gsub(/\s{2,}/, ' ').sub(/\n/, '')
|
85
85
|
end
|
86
86
|
end
|
87
|
-
|
87
|
+
|
88
88
|
module EmailHelpers
|
89
89
|
def sanitize_header(charset, name, *values)
|
90
90
|
header_sanitizer(name).call(charset, *values)
|
91
91
|
end
|
92
|
-
|
92
|
+
|
93
93
|
def header_sanitizer(name)
|
94
94
|
Util.default_header_sanitizers[name]
|
95
95
|
end
|
96
|
-
|
96
|
+
|
97
97
|
def html_to_text(html)
|
98
98
|
HtmlTextExtraction.new(html).execute
|
99
99
|
end
|
100
|
-
|
100
|
+
|
101
101
|
def normalize_new_lines(text)
|
102
102
|
text.to_s.gsub(/\r\n?/, "\n")
|
103
103
|
end
|
104
|
-
|
104
|
+
|
105
105
|
def subscriber_name_and_address(subscriber)
|
106
106
|
a = subscriber.email_address
|
107
107
|
a = "#{subscriber.display_name} #{bracket(a)}" if subscriber.respond_to?(:display_name)
|
108
108
|
a
|
109
109
|
end
|
110
|
-
|
110
|
+
|
111
|
+
AUTO_LINK_RE = %r{
|
112
|
+
( https?:// | www\. )
|
113
|
+
[^\s<]+
|
114
|
+
}x unless const_defined?(:AUTO_LINK_RE)
|
115
|
+
|
116
|
+
BRACKETS = { ']' => '[', ')' => '(', '}' => '{' }
|
117
|
+
|
118
|
+
# Turns all urls into clickable links. If a block is given, each url
|
119
|
+
# is yielded and the result is used as the link text.
|
120
|
+
def auto_link_urls(text)
|
121
|
+
text.gsub(AUTO_LINK_RE) do
|
122
|
+
href = $&
|
123
|
+
punctuation = ''
|
124
|
+
left, right = $`, $'
|
125
|
+
# detect already linked URLs and URLs in the middle of a tag
|
126
|
+
if left =~ /<[^>]+$/ && right =~ /^[^>]*>/
|
127
|
+
# do not change string; URL is alreay linked
|
128
|
+
href
|
129
|
+
else
|
130
|
+
# don't include trailing punctuation character as part of the URL
|
131
|
+
if href.sub!(/[^\w\/-]$/, '') and punctuation = $& and opening = BRACKETS[punctuation]
|
132
|
+
if href.scan(opening).size > href.scan(punctuation).size
|
133
|
+
href << punctuation
|
134
|
+
punctuation = ''
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
link_text = block_given?? yield(href) : href
|
139
|
+
href = 'http://' + href unless href.index('http') == 0
|
140
|
+
|
141
|
+
%Q(<a href="#{href}">#{link_text}</a>)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
|
111
147
|
BRACKETS_RE = /\A<(.*?)>\Z/
|
112
148
|
def bracket(string)
|
113
149
|
string.blank? || string =~ BRACKETS_RE ? string : "<#{string}>"
|
114
150
|
end
|
115
|
-
|
151
|
+
|
116
152
|
def remove_brackets(string)
|
117
153
|
string =~ BRACKETS_RE ? $1 : string
|
118
154
|
end
|
119
|
-
|
155
|
+
|
120
156
|
REGARD_RE = /(^|[^\w])re: /i
|
121
157
|
def remove_regard(string)
|
122
158
|
while string =~ REGARD_RE
|
@@ -124,7 +160,7 @@ module MList
|
|
124
160
|
end
|
125
161
|
string.strip
|
126
162
|
end
|
127
|
-
|
163
|
+
|
128
164
|
def text_to_html(text)
|
129
165
|
lines = normalize_new_lines(text).split("\n")
|
130
166
|
lines.collect! do |line|
|
@@ -136,7 +172,7 @@ module MList
|
|
136
172
|
end
|
137
173
|
lines.join("<br />\n")
|
138
174
|
end
|
139
|
-
|
175
|
+
|
140
176
|
def text_to_quoted(text)
|
141
177
|
lines = normalize_new_lines(text).split("\n")
|
142
178
|
lines.collect! do |line|
|
@@ -144,12 +180,12 @@ module MList
|
|
144
180
|
end
|
145
181
|
lines.join("\n")
|
146
182
|
end
|
147
|
-
|
183
|
+
|
148
184
|
HTML_ESCAPE = { '&' => '&', '>' => '>', '<' => '<', '"' => '"' }
|
149
185
|
def escape_once(text)
|
150
186
|
text.gsub(/[\"><]|&(?!([a-zA-Z]+|(#\d+));)/) { |special| HTML_ESCAPE[special] }
|
151
187
|
end
|
152
188
|
end
|
153
|
-
|
189
|
+
|
154
190
|
end
|
155
|
-
end
|
191
|
+
end
|
data/lib/mlist/util/quoting.rb
CHANGED
@@ -1,17 +1,17 @@
|
|
1
1
|
module MList
|
2
2
|
module Util
|
3
|
-
|
3
|
+
|
4
4
|
module TMailReaders
|
5
5
|
def date
|
6
6
|
if date = tmail.header_string('date')
|
7
7
|
Time.parse(date)
|
8
8
|
end
|
9
9
|
end
|
10
|
-
|
10
|
+
|
11
11
|
def from_address
|
12
12
|
tmail.from.first.downcase
|
13
13
|
end
|
14
|
-
|
14
|
+
|
15
15
|
def html
|
16
16
|
case tmail.content_type
|
17
17
|
when 'text/html'
|
@@ -21,27 +21,39 @@ module MList
|
|
21
21
|
html_part.body.strip if html_part
|
22
22
|
end
|
23
23
|
end
|
24
|
-
|
24
|
+
|
25
25
|
def identifier
|
26
26
|
remove_brackets(tmail.header_string('message-id'))
|
27
27
|
end
|
28
|
-
|
28
|
+
|
29
29
|
def mailer
|
30
30
|
tmail.header_string('x-mailer')
|
31
31
|
end
|
32
|
-
|
32
|
+
|
33
33
|
def text
|
34
34
|
text_content = ''
|
35
35
|
extract_text_content(tmail, text_content)
|
36
36
|
return text_content unless text_content.blank?
|
37
|
-
|
37
|
+
|
38
38
|
html_content = ''
|
39
39
|
extract_html_content(tmail, html_content)
|
40
40
|
return html_to_text(html_content) unless html_content.blank?
|
41
|
-
|
41
|
+
|
42
42
|
return nil
|
43
43
|
end
|
44
|
-
|
44
|
+
|
45
|
+
# Answers the first text/html part it can find, the tmail itself if
|
46
|
+
# it's content type is text/html.
|
47
|
+
#
|
48
|
+
def text_html_part(part = tmail)
|
49
|
+
case part.content_type
|
50
|
+
when 'text/html'
|
51
|
+
part
|
52
|
+
when 'multipart/alternative'
|
53
|
+
part.parts.detect {|part| text_html_part(part)}
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
45
57
|
# Answers the first text/plain part it can find, the tmail itself if
|
46
58
|
# it's content type is text/plain.
|
47
59
|
#
|
@@ -53,7 +65,7 @@ module MList
|
|
53
65
|
part.parts.detect {|part| text_plain_part(part)}
|
54
66
|
end
|
55
67
|
end
|
56
|
-
|
68
|
+
|
57
69
|
private
|
58
70
|
def extract_html_content(part, collector)
|
59
71
|
case part.content_type
|
@@ -63,7 +75,7 @@ module MList
|
|
63
75
|
part.parts.each {|part| extract_html_content(part, collector)}
|
64
76
|
end
|
65
77
|
end
|
66
|
-
|
78
|
+
|
67
79
|
def extract_text_content(part, collector)
|
68
80
|
case part.content_type
|
69
81
|
when 'text/plain'
|
@@ -73,16 +85,16 @@ module MList
|
|
73
85
|
end
|
74
86
|
end
|
75
87
|
end
|
76
|
-
|
88
|
+
|
77
89
|
module TMailWriters
|
78
90
|
def charset
|
79
91
|
'utf-8'
|
80
92
|
end
|
81
|
-
|
93
|
+
|
82
94
|
def delete_header(name)
|
83
95
|
tmail[name] = nil
|
84
96
|
end
|
85
|
-
|
97
|
+
|
86
98
|
def headers=(updates)
|
87
99
|
updates.each do |k,v|
|
88
100
|
if TMail::Mail::ALLOW_MULTIPLE.include?(k.downcase)
|
@@ -92,7 +104,7 @@ module MList
|
|
92
104
|
end
|
93
105
|
end
|
94
106
|
end
|
95
|
-
|
107
|
+
|
96
108
|
# Add another value for the named header, it's position being earlier in
|
97
109
|
# the email than those that are already present. This will raise an error
|
98
110
|
# if the header does not allow multiple values according to
|
@@ -104,35 +116,35 @@ module MList
|
|
104
116
|
tmail[name] = sanitize_header(charset, name, value)
|
105
117
|
tmail[name] = tmail[name] + original
|
106
118
|
end
|
107
|
-
|
119
|
+
|
108
120
|
def write_header(name, value)
|
109
121
|
tmail[name] = sanitize_header(charset, name, value)
|
110
122
|
end
|
111
|
-
|
123
|
+
|
112
124
|
def to=(recipient_addresses)
|
113
125
|
tmail.to = sanitize_header(charset, 'to', recipient_addresses)
|
114
126
|
end
|
115
|
-
|
127
|
+
|
116
128
|
def bcc=(recipient_addresses)
|
117
129
|
tmail.bcc = sanitize_header(charset, 'bcc', recipient_addresses)
|
118
130
|
end
|
119
|
-
|
131
|
+
|
120
132
|
def from=(from_address)
|
121
133
|
tmail.from = sanitize_header(charset, 'from', from_address)
|
122
134
|
end
|
123
|
-
|
135
|
+
|
124
136
|
def in_reply_to=(*values)
|
125
137
|
tmail.in_reply_to = sanitize_header(charset, 'in-reply-to', *values)
|
126
138
|
end
|
127
|
-
|
139
|
+
|
128
140
|
def mailer=(value)
|
129
141
|
write_header('x-mailer', value)
|
130
142
|
end
|
131
|
-
|
143
|
+
|
132
144
|
def message_id=(value)
|
133
145
|
tmail.message_id = sanitize_header(charset, 'message-id', value)
|
134
146
|
end
|
135
147
|
end
|
136
|
-
|
148
|
+
|
137
149
|
end
|
138
|
-
end
|
150
|
+
end
|
@@ -4,38 +4,38 @@ describe MList::MailList do
|
|
4
4
|
class ManagerList
|
5
5
|
include MList::List
|
6
6
|
end
|
7
|
-
|
7
|
+
|
8
8
|
before do
|
9
9
|
@manager_list = ManagerList.new
|
10
10
|
stub(@manager_list).label {'Discussions'}
|
11
11
|
stub(@manager_list).address {'list_one@example.com'}
|
12
12
|
stub(@manager_list).list_id {'list_one@example.com'}
|
13
|
-
|
13
|
+
|
14
14
|
@subscriber_one = MList::EmailSubscriber.new('adam@nomail.net')
|
15
15
|
@subscriber_two = MList::EmailSubscriber.new('john@example.com')
|
16
16
|
stub(@manager_list).subscribers {[@subscriber_one, @subscriber_two]}
|
17
|
-
|
17
|
+
|
18
18
|
@outgoing_server = MList::EmailServer::Fake.new
|
19
19
|
@mail_list = MList::MailList.create!(
|
20
20
|
:manager_list => @manager_list,
|
21
21
|
:outgoing_server => @outgoing_server)
|
22
22
|
end
|
23
|
-
|
23
|
+
|
24
24
|
it 'should not require the manager list be an ActiveRecord type' do
|
25
25
|
@mail_list.list.should == @manager_list
|
26
26
|
@mail_list.manager_list.should be_nil
|
27
27
|
end
|
28
|
-
|
28
|
+
|
29
29
|
it 'should have messages counted' do
|
30
30
|
MList::Message.reflect_on_association(:mail_list).counter_cache_column.should == :messages_count
|
31
31
|
MList::MailList.column_names.should include('messages_count')
|
32
32
|
end
|
33
|
-
|
33
|
+
|
34
34
|
it 'should have threads counted' do
|
35
35
|
MList::Thread.reflect_on_association(:mail_list).counter_cache_column.should == :threads_count
|
36
36
|
MList::MailList.column_names.should include('threads_count')
|
37
37
|
end
|
38
|
-
|
38
|
+
|
39
39
|
it 'should delete email not referenced by other lists' do
|
40
40
|
email_in_other = MList::Email.create!(:tmail => tmail_fixture('single_list'))
|
41
41
|
email_not_other = MList::Email.create!(:tmail => tmail_fixture('single_list'))
|
@@ -48,7 +48,7 @@ describe MList::MailList do
|
|
48
48
|
MList::Email.exists?(email_in_other).should be_true
|
49
49
|
MList::Email.exists?(email_not_other).should be_false
|
50
50
|
end
|
51
|
-
|
51
|
+
|
52
52
|
describe 'post' do
|
53
53
|
it 'should allow posting a new message to the list' do
|
54
54
|
lambda do
|
@@ -60,12 +60,12 @@ describe MList::MailList do
|
|
60
60
|
)
|
61
61
|
end.should change(MList::Message, :count).by(1)
|
62
62
|
end.should change(MList::Thread, :count).by(1)
|
63
|
-
|
63
|
+
|
64
64
|
tmail = @outgoing_server.deliveries.last
|
65
65
|
tmail.subject.should =~ /I'm a Program!/
|
66
66
|
tmail.from.should == ['adam@nomail.net']
|
67
67
|
end
|
68
|
-
|
68
|
+
|
69
69
|
it 'should answer the message for use by the application' do
|
70
70
|
@mail_list.post(
|
71
71
|
:subscriber => @subscriber_one,
|
@@ -73,7 +73,7 @@ describe MList::MailList do
|
|
73
73
|
:text => 'Are you a programmer or what?'
|
74
74
|
).should be_instance_of(MList::Message)
|
75
75
|
end
|
76
|
-
|
76
|
+
|
77
77
|
it 'should allow posting a reply to an existing message' do
|
78
78
|
@mail_list.process_email(MList::Email.new(:tmail => tmail_fixture('single_list')), @subscriber_one)
|
79
79
|
existing_message = @mail_list.messages.last
|
@@ -89,7 +89,7 @@ describe MList::MailList do
|
|
89
89
|
new_message = MList::Message.last
|
90
90
|
new_message.subject.should == "Re: Test"
|
91
91
|
end
|
92
|
-
|
92
|
+
|
93
93
|
it 'should not associate a posting to a parent if not reply' do
|
94
94
|
@mail_list.process_email(MList::Email.new(:tmail => tmail_fixture('single_list')), @subscriber_one)
|
95
95
|
lambda do
|
@@ -105,7 +105,7 @@ describe MList::MailList do
|
|
105
105
|
message.parent.should be_nil
|
106
106
|
message.parent_identifier.should be_nil
|
107
107
|
end
|
108
|
-
|
108
|
+
|
109
109
|
it 'should capture the message-id of delivered email' do
|
110
110
|
message = @mail_list.post(
|
111
111
|
:subscriber => @subscriber_one,
|
@@ -113,91 +113,91 @@ describe MList::MailList do
|
|
113
113
|
:text => 'Email must have a message id for threading')
|
114
114
|
message.reload.identifier.should_not be_nil
|
115
115
|
end
|
116
|
-
|
116
|
+
|
117
117
|
it 'should copy the subscriber if desired' do
|
118
118
|
@mail_list.post(
|
119
119
|
:subscriber => @subscriber_one,
|
120
120
|
:subject => 'Copy Me',
|
121
121
|
:text => 'Email should be sent to subscriber if desired',
|
122
122
|
:copy_sender => true)
|
123
|
-
|
123
|
+
|
124
124
|
tmail = @outgoing_server.deliveries.last
|
125
125
|
tmail.bcc.should include(@subscriber_one.email_address)
|
126
126
|
end
|
127
|
-
|
127
|
+
|
128
128
|
it 'should not copy the subscriber if undesired and list includes the subscriber' do
|
129
129
|
# The MList::List implementor may include the sending subscriber
|
130
130
|
stub(@manager_list).recipients {[@subscriber_one, @subscriber_two]}
|
131
|
-
|
131
|
+
|
132
132
|
@mail_list.post(
|
133
133
|
:subscriber => @subscriber_one,
|
134
134
|
:subject => 'Do Not Copy Me',
|
135
135
|
:text => 'Email should not be sent to subscriber if undesired',
|
136
136
|
:copy_sender => false)
|
137
|
-
|
137
|
+
|
138
138
|
tmail = @outgoing_server.deliveries.last
|
139
139
|
tmail.bcc.should_not include(@subscriber_one.email_address)
|
140
140
|
end
|
141
141
|
end
|
142
|
-
|
142
|
+
|
143
143
|
describe 'message storage' do
|
144
144
|
def process_post
|
145
145
|
@mail_list.process_email(MList::Email.new(:tmail => @post_tmail), @subscriber)
|
146
146
|
MList::Message.last
|
147
147
|
end
|
148
|
-
|
148
|
+
|
149
149
|
before do
|
150
150
|
@post_tmail = tmail_fixture('single_list')
|
151
151
|
@subscriber = @subscriber_one
|
152
152
|
end
|
153
|
-
|
153
|
+
|
154
154
|
it 'should not include list label in subject' do
|
155
155
|
@post_tmail.subject = '[Discussions] Test'
|
156
156
|
process_post.subject.should == 'Test'
|
157
157
|
end
|
158
|
-
|
158
|
+
|
159
159
|
it 'should not include list label in reply subject' do
|
160
160
|
@post_tmail.subject = 'Re: [Discussions] Test'
|
161
161
|
process_post.subject.should == 'Re: Test'
|
162
162
|
end
|
163
|
-
|
163
|
+
|
164
164
|
it 'should not bother labels it does not understand in subject' do
|
165
165
|
@post_tmail.subject = '[Ann] Test'
|
166
166
|
process_post.subject.should == '[Ann] Test'
|
167
167
|
end
|
168
|
-
|
168
|
+
|
169
169
|
it 'should not bother labels it does not understand in reply subject' do
|
170
170
|
@post_tmail.subject = 'Re: [Ann] Test'
|
171
171
|
process_post.subject.should == 'Re: [Ann] Test'
|
172
172
|
end
|
173
|
-
|
173
|
+
|
174
174
|
it 'should be careful of multiple re:' do
|
175
175
|
@post_tmail.subject = 'Re: [Ann] RE: Test'
|
176
176
|
process_post.subject.should == 'Re: [Ann] Test'
|
177
177
|
end
|
178
178
|
end
|
179
|
-
|
179
|
+
|
180
180
|
describe 'finding parent message' do
|
181
181
|
def email(path)
|
182
182
|
MList::Email.new(:tmail => tmail_fixture(path))
|
183
183
|
end
|
184
|
-
|
184
|
+
|
185
185
|
before do
|
186
186
|
@parent_message = MList::Message.new
|
187
187
|
end
|
188
|
-
|
188
|
+
|
189
189
|
it 'should be nil if none found' do
|
190
190
|
do_not_call(@mail_list.messages).find
|
191
191
|
@mail_list.find_parent_message(email('single_list')).should be_nil
|
192
192
|
end
|
193
|
-
|
193
|
+
|
194
194
|
it 'should use in-reply-to field when present' do
|
195
195
|
mock(@mail_list.messages).find(:first, :conditions => [
|
196
196
|
'identifier = ?', 'F5F9DC55-CB54-4F2C-9B46-A05F241BCF22@recursivecreative.com'
|
197
197
|
]) { @parent_message }
|
198
198
|
@mail_list.find_parent_message(email('single_list_reply')).should == @parent_message
|
199
199
|
end
|
200
|
-
|
200
|
+
|
201
201
|
it 'should be references field if present and no in-reply-to' do
|
202
202
|
tmail = tmail_fixture('single_list_reply')
|
203
203
|
tmail['in-reply-to'] = nil
|
@@ -206,7 +206,7 @@ describe MList::MailList do
|
|
206
206
|
:order => 'created_at desc') { @parent_message }
|
207
207
|
@mail_list.find_parent_message(MList::Email.new(:tmail => tmail)).should == @parent_message
|
208
208
|
end
|
209
|
-
|
209
|
+
|
210
210
|
describe 'by subject' do
|
211
211
|
def search_subject(subject = nil)
|
212
212
|
simple_matcher("search by the subject '#{subject}'") do |email|
|
@@ -223,7 +223,7 @@ describe MList::MailList do
|
|
223
223
|
!subject.nil?
|
224
224
|
end
|
225
225
|
end
|
226
|
-
|
226
|
+
|
227
227
|
before do
|
228
228
|
@parent_message = MList::Message.new
|
229
229
|
@mail_list = MList::MailList.new
|
@@ -231,16 +231,16 @@ describe MList::MailList do
|
|
231
231
|
@reply_tmail = tmail_fixture('single_list')
|
232
232
|
@reply_email = MList::Email.new(:tmail => @reply_tmail)
|
233
233
|
end
|
234
|
-
|
234
|
+
|
235
235
|
it 'should be employed if it has "re:" in it' do
|
236
236
|
@reply_tmail.subject = "Re: Test"
|
237
237
|
@reply_email.should search_subject('Test')
|
238
238
|
end
|
239
|
-
|
239
|
+
|
240
240
|
it 'should not be employed when no "re:"' do
|
241
241
|
@reply_email.should_not search_subject
|
242
242
|
end
|
243
|
-
|
243
|
+
|
244
244
|
['RE: [list name] Re: Test', 'Re: [list name] Re: [list name] Test', '[list name] Re: Test'].each do |subject|
|
245
245
|
it "should handle '#{subject}'" do
|
246
246
|
@reply_tmail.subject = subject
|
@@ -249,20 +249,20 @@ describe MList::MailList do
|
|
249
249
|
end
|
250
250
|
end
|
251
251
|
end
|
252
|
-
|
252
|
+
|
253
253
|
describe 'delivery' do
|
254
254
|
include MList::Util::EmailHelpers
|
255
|
-
|
255
|
+
|
256
256
|
def process_post
|
257
257
|
@mail_list.process_email(MList::Email.new(:source => @post_tmail.to_s), @subscriber)
|
258
258
|
@outgoing_server.deliveries.last
|
259
259
|
end
|
260
|
-
|
260
|
+
|
261
261
|
before do
|
262
262
|
@post_tmail = tmail_fixture('single_list')
|
263
263
|
@subscriber = @subscriber_one
|
264
264
|
end
|
265
|
-
|
265
|
+
|
266
266
|
it 'should be blind copied to recipients' do
|
267
267
|
mock.proxy(@mail_list.messages).build(anything) do |message|
|
268
268
|
mock(message.delivery).bcc=(%w(john@example.com))
|
@@ -270,7 +270,7 @@ describe MList::MailList do
|
|
270
270
|
end
|
271
271
|
process_post
|
272
272
|
end
|
273
|
-
|
273
|
+
|
274
274
|
it 'should not deliver to addresses found in the to header' do
|
275
275
|
@post_tmail.to = ['john@example.com', 'list_one@example.com']
|
276
276
|
mock.proxy(@mail_list.messages).build(anything) do |message|
|
@@ -279,7 +279,7 @@ describe MList::MailList do
|
|
279
279
|
end
|
280
280
|
process_post
|
281
281
|
end
|
282
|
-
|
282
|
+
|
283
283
|
it 'should not deliver to addresses found in the cc header' do
|
284
284
|
@post_tmail.cc = ['john@example.com']
|
285
285
|
mock.proxy(@mail_list.messages).build(anything) do |message|
|
@@ -288,88 +288,88 @@ describe MList::MailList do
|
|
288
288
|
end
|
289
289
|
process_post
|
290
290
|
end
|
291
|
-
|
291
|
+
|
292
292
|
it 'should use list address as reply-to by default' do
|
293
293
|
process_post.should have_header('reply-to', 'Discussions <list_one@example.com>')
|
294
294
|
end
|
295
|
-
|
295
|
+
|
296
296
|
it 'should use subscriber address as reply-to if list says to not use address' do
|
297
297
|
mock(@manager_list).reply_to_list? { false }
|
298
298
|
process_post.should have_header('reply-to', 'adam@nomail.net')
|
299
299
|
end
|
300
|
-
|
300
|
+
|
301
301
|
it 'should use the reply-to already in an email - should not override it' do
|
302
302
|
@post_tmail['reply-to'] = 'theotheradam@nomail.net'
|
303
303
|
process_post.should have_header('reply-to', 'theotheradam@nomail.net')
|
304
304
|
end
|
305
|
-
|
305
|
+
|
306
306
|
it 'should set x-beenthere on emails it delivers to keep from re-posting them' do
|
307
307
|
process_post.should have_header('x-beenthere', 'list_one@example.com')
|
308
308
|
end
|
309
|
-
|
309
|
+
|
310
310
|
it 'should not remove any existing x-beenthere headers' do
|
311
311
|
@post_tmail['x-beenthere'] = 'somewhere@nomail.net'
|
312
312
|
process_post.should have_header('x-beenthere', %w(list_one@example.com somewhere@nomail.net))
|
313
313
|
end
|
314
|
-
|
314
|
+
|
315
315
|
it 'should not modify existing headers' do
|
316
316
|
@post_tmail['x-something-custom'] = 'existing'
|
317
317
|
process_post.should have_header('x-something-custom', 'existing')
|
318
318
|
end
|
319
|
-
|
319
|
+
|
320
320
|
it 'should delete Return-Receipt-To headers since they cause clients to spam the list (the sender address)' do
|
321
321
|
@post_tmail['return-receipt-to'] = 'somewhere@nomail.net'
|
322
322
|
process_post.should_not have_header('return-receipt-to')
|
323
323
|
end
|
324
|
-
|
324
|
+
|
325
325
|
it 'should not have any cc addresses' do
|
326
326
|
@post_tmail['cc'] = 'billybob@anywhere.com'
|
327
327
|
process_post.should_not have_header('cc')
|
328
328
|
end
|
329
|
-
|
329
|
+
|
330
330
|
it 'should prefix the list label to the subject of messages' do
|
331
331
|
process_post.subject.should == '[Discussions] Test'
|
332
332
|
end
|
333
|
-
|
333
|
+
|
334
334
|
it 'should move the list label to the front of subjects that already include the label' do
|
335
335
|
@post_tmail.subject = 'Re: [Discussions] Test'
|
336
336
|
process_post.subject.should == 'Re: [Discussions] Test'
|
337
337
|
end
|
338
|
-
|
338
|
+
|
339
339
|
it 'should remove multiple occurrences of Re:' do
|
340
340
|
@post_tmail.subject = 'Re: [Discussions] Re: Test'
|
341
341
|
process_post.subject.should == 'Re: [Discussions] Test'
|
342
342
|
end
|
343
|
-
|
343
|
+
|
344
344
|
it 'should remove DomainKey-Signature headers so that we can sign the redistribution' do
|
345
345
|
@post_tmail['DomainKey-Signature'] = "a whole bunch of junk"
|
346
346
|
process_post.should_not have_header('domainkey-signature')
|
347
347
|
end
|
348
|
-
|
348
|
+
|
349
349
|
it 'should remove DKIM-Signature headers so that we can sign the redistribution' do
|
350
350
|
@post_tmail['DKIM-Signature'] = "a whole bunch of junk"
|
351
351
|
process_post.should_not have_header('dkim-signature')
|
352
352
|
end
|
353
|
-
|
353
|
+
|
354
354
|
it 'should capture the new message-ids' do
|
355
355
|
delivered = process_post
|
356
356
|
delivered.header_string('message-id').should_not be_blank
|
357
357
|
MList::Message.last.identifier.should == remove_brackets(delivered.header_string('message-id'))
|
358
358
|
delivered.header_string('message-id').should_not match(/F5F9DC55-CB54-4F2C-9B46-A05F241BCF22@recursivecreative\.com/)
|
359
359
|
end
|
360
|
-
|
360
|
+
|
361
361
|
it 'should maintain the content-id part headers (inline images, etc)' do
|
362
362
|
@post_tmail = tmail_fixture('embedded_content')
|
363
363
|
process_post.parts[1].parts[1]['content-id'].to_s.should == "<CF68EC17-F8ED-478A-A4A1-AEBF165A8830/bg_pattern.jpg>"
|
364
364
|
end
|
365
|
-
|
365
|
+
|
366
366
|
it 'should add standard list headers when they are available' do
|
367
367
|
stub(@manager_list).help_url {'http://list_manager.example.com/help'}
|
368
368
|
stub(@manager_list).subscribe_url {'http://list_manager.example.com/subscribe'}
|
369
369
|
stub(@manager_list).unsubscribe_url {'http://list_manager.example.com/unsubscribe'}
|
370
370
|
stub(@manager_list).owner_url {"<mailto:list_manager@example.com>\n(Jimmy Fish)"}
|
371
371
|
stub(@manager_list).archive_url {'http://list_manager.example.com/archive'}
|
372
|
-
|
372
|
+
|
373
373
|
tmail = process_post
|
374
374
|
tmail.should have_headers(
|
375
375
|
'list-id' => "<list_one@example.com>",
|
@@ -385,33 +385,39 @@ describe MList::MailList do
|
|
385
385
|
)
|
386
386
|
tmail.header_string('x-mlist-version').should =~ /\d+\.\d+\.\d+/
|
387
387
|
end
|
388
|
-
|
388
|
+
|
389
389
|
it 'should not add list headers that are not available or nil' do
|
390
390
|
stub(@manager_list).help_url {nil}
|
391
391
|
delivery = process_post
|
392
392
|
delivery.should_not have_header('list-help')
|
393
393
|
delivery.should_not have_header('list-subscribe')
|
394
394
|
end
|
395
|
-
|
395
|
+
|
396
396
|
it 'should append the list footer to text/plain emails' do
|
397
397
|
@post_tmail.body = "My Email\n\n\n\n\n"
|
398
398
|
mock(@manager_list).footer_content(is_a(MList::Message)) { 'my footer' }
|
399
399
|
process_post.body.should == "My Email\n\n\n\n\n#{MList::MailList::FOOTER_BLOCK_START}\nmy footer\n#{MList::MailList::FOOTER_BLOCK_END}"
|
400
400
|
end
|
401
|
-
|
401
|
+
|
402
402
|
it 'should append the list footer to multipart/alternative, text/plain part of emails' do
|
403
403
|
@post_tmail = tmail_fixture('content_types/multipart_alternative_simple')
|
404
404
|
mock(@manager_list).footer_content(is_a(MList::Message)) { 'my footer' }
|
405
405
|
process_post.parts[0].body.should match(/#{MList::MailList::FOOTER_BLOCK_START}\nmy footer\n#{MList::MailList::FOOTER_BLOCK_END}/)
|
406
406
|
end
|
407
|
-
|
407
|
+
|
408
|
+
it 'should append the list footer to multipart/alternative, text/html part of emails' do
|
409
|
+
@post_tmail = tmail_fixture('content_types/multipart_alternative_simple')
|
410
|
+
mock(@manager_list).footer_content(is_a(MList::Message)) { "my footer\nis here\nhttp://links/here" }
|
411
|
+
process_post.parts[1].body.should match(/<p>#{MList::MailList::FOOTER_BLOCK_START}<br \/>\nmy footer<br \/>\nis here<br \/>\n<a href="http:\/\/links\/here">http:\/\/links\/here<\/a><br \/>\n#{MList::MailList::FOOTER_BLOCK_END}<\/p>/)
|
412
|
+
end
|
413
|
+
|
408
414
|
it 'should handle whitespace well when appending footer' do
|
409
415
|
@post_tmail.body = "My Email"
|
410
416
|
mock(@manager_list).footer_content(is_a(MList::Message)) { 'my footer' }
|
411
417
|
process_post.body.should == "My Email\n\n#{MList::MailList::FOOTER_BLOCK_START}\nmy footer\n#{MList::MailList::FOOTER_BLOCK_END}"
|
412
418
|
end
|
413
|
-
|
414
|
-
it 'should strip out any existing footers from the list' do
|
419
|
+
|
420
|
+
it 'should strip out any existing text footers from the list' do
|
415
421
|
mock(@manager_list).footer_content(is_a(MList::Message)) { 'my footer' }
|
416
422
|
@post_tmail.body = %{My Email
|
417
423
|
|
@@ -429,32 +435,53 @@ this is without any in front
|
|
429
435
|
}
|
430
436
|
process_post.body.should == "My Email\n\n#{MList::MailList::FOOTER_BLOCK_START}\nmy footer\n#{MList::MailList::FOOTER_BLOCK_END}"
|
431
437
|
end
|
432
|
-
|
438
|
+
|
439
|
+
it 'should strip out any existing html footers from the list' do
|
440
|
+
@post_tmail = tmail_fixture('content_types/multipart_alternative_simple')
|
441
|
+
mock(@manager_list).footer_content(is_a(MList::Message)) { 'my footer' }
|
442
|
+
@post_tmail.parts[1].body = %{<p>My Email</p>
|
443
|
+
<blockquote>
|
444
|
+
<p> Stuff in my email</p><p> #{MList::MailList::FOOTER_BLOCK_START}
|
445
|
+
> > content at front shouldn't matter
|
446
|
+
> > #{MList::MailList::FOOTER_BLOCK_END} </p>
|
447
|
+
|
448
|
+
>> not in our p!<p>#{MList::MailList::FOOTER_BLOCK_START}
|
449
|
+
>> this is fine to be removed
|
450
|
+
>> #{MList::MailList::FOOTER_BLOCK_END}</p>
|
451
|
+
|
452
|
+
<p>#{MList::MailList::FOOTER_BLOCK_START}
|
453
|
+
this is without any in front
|
454
|
+
#{MList::MailList::FOOTER_BLOCK_END}
|
455
|
+
</p>
|
456
|
+
}
|
457
|
+
process_post.parts[1].body.should == "<p>My Email</p>\n<blockquote>\n <p> Stuff in my email</p>\n\n>> not in our p!<p>#{MList::MailList::FOOTER_BLOCK_START}<br />\nmy footer<br />\n#{MList::MailList::FOOTER_BLOCK_END}</p>"
|
458
|
+
end
|
459
|
+
|
433
460
|
describe 'time' do
|
434
461
|
include TMail::TextUtils
|
435
|
-
|
462
|
+
|
436
463
|
before do
|
437
464
|
@old_zone_default = Time.zone_default
|
438
465
|
@system_time = Time.parse('Thu, 2 Apr 2009 15:22:04')
|
439
466
|
mock(Time).now.times(any_times) { @system_time }
|
440
467
|
end
|
441
|
-
|
468
|
+
|
442
469
|
after do
|
443
470
|
Time.zone_default = @old_zone_default
|
444
471
|
end
|
445
|
-
|
472
|
+
|
446
473
|
it 'should keep date of email post' do
|
447
474
|
@post_tmail['date'] = 'Thu, 2 Apr 2009 15:22:04 -0400'
|
448
475
|
process_post.header_string('date').should == 'Thu, 2 Apr 2009 15:22:04 -0400'
|
449
476
|
end
|
450
|
-
|
477
|
+
|
451
478
|
it 'should store the delivery time as created_at of message record' do
|
452
479
|
Time.zone_default = 'Pacific Time (US & Canada)'
|
453
480
|
@post_tmail['date'] = 'Wed, 1 Apr 2009 15:22:04 -0400'
|
454
481
|
process_post.header_string('date').should == 'Wed, 1 Apr 2009 15:22:04 -0400'
|
455
482
|
MList::Message.last.created_at.should == @system_time
|
456
483
|
end
|
457
|
-
|
484
|
+
|
458
485
|
# I think that what TMail is doing is evil, but it's reference to
|
459
486
|
# a ruby-talk discussion leads to Japanese, which I cannot read.
|
460
487
|
# I'd prefer that it leave the problem of timezones up to the client,
|
@@ -466,4 +493,4 @@ this is without any in front
|
|
466
493
|
end
|
467
494
|
end
|
468
495
|
end
|
469
|
-
end
|
496
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,12 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mlist
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 1
|
8
|
+
- 12
|
9
|
+
version: 0.1.12
|
5
10
|
platform: ruby
|
6
11
|
authors:
|
7
12
|
- Adam Williams
|
@@ -9,7 +14,7 @@ autorequire:
|
|
9
14
|
bindir: bin
|
10
15
|
cert_chain: []
|
11
16
|
|
12
|
-
date: 2010-
|
17
|
+
date: 2010-03-26 00:00:00 -04:00
|
13
18
|
default_executable:
|
14
19
|
dependencies: []
|
15
20
|
|
@@ -67,18 +72,20 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
67
72
|
requirements:
|
68
73
|
- - ">="
|
69
74
|
- !ruby/object:Gem::Version
|
75
|
+
segments:
|
76
|
+
- 0
|
70
77
|
version: "0"
|
71
|
-
version:
|
72
78
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
73
79
|
requirements:
|
74
80
|
- - ">="
|
75
81
|
- !ruby/object:Gem::Version
|
82
|
+
segments:
|
83
|
+
- 0
|
76
84
|
version: "0"
|
77
|
-
version:
|
78
85
|
requirements: []
|
79
86
|
|
80
87
|
rubyforge_project:
|
81
|
-
rubygems_version: 1.3.
|
88
|
+
rubygems_version: 1.3.6
|
82
89
|
signing_key:
|
83
90
|
specification_version: 3
|
84
91
|
summary: A Ruby mailing list library designed to be integrated into other applications.
|