aiwilliams-mlist 0.1.6 → 0.1.7
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +11 -0
- data/VERSION.yml +2 -2
- data/lib/mlist/email.rb +15 -1
- data/lib/mlist/email_post.rb +14 -5
- data/lib/mlist/list.rb +25 -9
- data/lib/mlist/mail_list.rb +25 -6
- data/lib/mlist/message.rb +10 -0
- data/lib/mlist/thread.rb +2 -2
- data/lib/mlist/util/email_helpers.rb +15 -2
- data/lib/mlist/util/tmail_methods.rb +0 -2
- data/rails/init.rb +20 -26
- metadata +4 -3
data/CHANGELOG
CHANGED
@@ -1,3 +1,14 @@
|
|
1
|
+
*0.1.7 [Bug Fixes, Delivery Improvements] (2009-08-14)
|
2
|
+
|
3
|
+
* is maintained as spacing in html_to_text conversions [aiwilliams]
|
4
|
+
* Fixed bug where delivered email included the original Cc header. This could cause all kinds of problems. [aiwilliams]
|
5
|
+
* Fixed bug where list addresses in Cc header were not be utilized in determining the lists to deliver to. [aiwilliams]
|
6
|
+
* Fixed bug where delivery date was left to the mercy of the Rails time configuration. Using Time.now. [aiwilliams]
|
7
|
+
* Manager list can indicate if reply-to should be list address or subscriber. [aiwilliams]
|
8
|
+
* Leaving the reply-to field intact when it already exists on incoming email. [aiwilliams]
|
9
|
+
* Messages will not be delivered to addresses found in the TO and CC fields in order to keep those recipients from getting two emails. [aiwilliams]
|
10
|
+
* Added ability to optionally copy sender. [aiwilliams]
|
11
|
+
|
1
12
|
*0.1.6 [Bug Fixes] (March 5, 2009)
|
2
13
|
|
3
14
|
* Messages are processed even when there are no recipients [aiwilliams]
|
data/VERSION.yml
CHANGED
data/lib/mlist/email.rb
CHANGED
@@ -10,6 +10,14 @@ module MList
|
|
10
10
|
tmail.header_string('x-beenthere') == list.address
|
11
11
|
end
|
12
12
|
|
13
|
+
def date
|
14
|
+
if date_from_email = super
|
15
|
+
return date_from_email
|
16
|
+
else
|
17
|
+
self.created_at ||= Time.now
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
13
21
|
def from
|
14
22
|
tmail.header_string('from')
|
15
23
|
end
|
@@ -17,7 +25,7 @@ module MList
|
|
17
25
|
# Answers the usable destination addresses of the email.
|
18
26
|
#
|
19
27
|
def list_addresses
|
20
|
-
bounce? ? tmail.header_string('to').match(/\Amlist-(.*)\Z/)[1] :
|
28
|
+
bounce? ? tmail.header_string('to').match(/\Amlist-(.*)\Z/)[1] : recipient_addresses
|
21
29
|
end
|
22
30
|
|
23
31
|
# Answers true if this email is a bounce.
|
@@ -48,6 +56,12 @@ module MList
|
|
48
56
|
end
|
49
57
|
end
|
50
58
|
|
59
|
+
# Answers the set of addresses found in the TO and CC fields of the email.
|
60
|
+
#
|
61
|
+
def recipient_addresses
|
62
|
+
(Array(tmail.to) + Array(tmail.cc)).collect(&:downcase).uniq
|
63
|
+
end
|
64
|
+
|
51
65
|
def respond_to?(method)
|
52
66
|
super || (method.to_s !~ /=\Z/ && tmail.respond_to?(method))
|
53
67
|
end
|
data/lib/mlist/email_post.rb
CHANGED
@@ -12,7 +12,7 @@ module MList
|
|
12
12
|
class EmailPost
|
13
13
|
include MList::Util::EmailHelpers
|
14
14
|
|
15
|
-
ATTRIBUTE_NAMES = %w(html text mailer subject subscriber)
|
15
|
+
ATTRIBUTE_NAMES = %w(copy_sender html text mailer subject subscriber)
|
16
16
|
ATTRIBUTE_NAMES.each do |attribute_name|
|
17
17
|
define_method(attribute_name) do
|
18
18
|
@attributes[attribute_name]
|
@@ -44,6 +44,10 @@ module MList
|
|
44
44
|
end
|
45
45
|
end
|
46
46
|
|
47
|
+
def copy_sender=(value)
|
48
|
+
@attributes['copy_sender'] = %w(true 1).include?(value.to_s)
|
49
|
+
end
|
50
|
+
|
47
51
|
def reply_to_message=(message)
|
48
52
|
if message
|
49
53
|
@parent_identifier = message.identifier
|
@@ -74,10 +78,7 @@ module MList
|
|
74
78
|
builder.references = [bracket(parent_identifier)]
|
75
79
|
end
|
76
80
|
|
77
|
-
from = subscriber
|
78
|
-
from = "#{subscriber.display_name} #{bracket(from)}" if subscriber.respond_to?(:display_name)
|
79
|
-
builder.from = from
|
80
|
-
|
81
|
+
builder.from = subscriber_name_and_address(subscriber)
|
81
82
|
builder.subject = subject
|
82
83
|
|
83
84
|
if html
|
@@ -94,6 +95,14 @@ module MList
|
|
94
95
|
|
95
96
|
# vvv ActiveRecord validations interface implementation vvv
|
96
97
|
|
98
|
+
def self.human_name(options = {})
|
99
|
+
self.name.humanize
|
100
|
+
end
|
101
|
+
|
102
|
+
def self.self_and_descendants_from_active_record #nodoc:
|
103
|
+
[self]
|
104
|
+
end
|
105
|
+
|
97
106
|
def self.human_attribute_name(attribute_key_name, options = {})
|
98
107
|
attribute_key_name.humanize
|
99
108
|
end
|
data/lib/mlist/list.rb
CHANGED
@@ -25,10 +25,10 @@ module MList
|
|
25
25
|
end
|
26
26
|
|
27
27
|
# Answers the footer content for this list. Default implementation is very
|
28
|
-
# simple
|
28
|
+
# simple.
|
29
29
|
#
|
30
30
|
def footer_content(message)
|
31
|
-
%Q{The "#{label}" mailing list\
|
31
|
+
%Q{The "#{label}" mailing list\nPost messages: #{post_url}}
|
32
32
|
end
|
33
33
|
|
34
34
|
# Answer a suitable label for the list, which will be used in various
|
@@ -67,6 +67,16 @@ module MList
|
|
67
67
|
nil
|
68
68
|
end
|
69
69
|
|
70
|
+
# Should the sender of an email be copied in the publication? Defaults to
|
71
|
+
# false. If _recipients_ includes the sender email address, it will be
|
72
|
+
# removed if this answers false.
|
73
|
+
#
|
74
|
+
# The sending subscriber is provided to support preferential answering.
|
75
|
+
#
|
76
|
+
def copy_sender?(subscriber)
|
77
|
+
false
|
78
|
+
end
|
79
|
+
|
70
80
|
# The web address of the list help site, nil if this is not supported.
|
71
81
|
#
|
72
82
|
def help_url
|
@@ -86,6 +96,13 @@ module MList
|
|
86
96
|
address
|
87
97
|
end
|
88
98
|
|
99
|
+
# Should the reply-to header be set to the list's address? Defaults to
|
100
|
+
# true. If false is returned, the reply-to will be the subscriber address.
|
101
|
+
#
|
102
|
+
def reply_to_list?
|
103
|
+
true
|
104
|
+
end
|
105
|
+
|
89
106
|
# The web url where subscriptions to this list may be created, nil if this
|
90
107
|
# is not supported.
|
91
108
|
#
|
@@ -100,16 +117,15 @@ module MList
|
|
100
117
|
nil
|
101
118
|
end
|
102
119
|
|
103
|
-
# A list is responsible for answering the recipient subscribers.
|
120
|
+
# A list is responsible for answering the recipient subscribers. The
|
121
|
+
# answer may or may not include the subscriber; _copy_sender?_ will be
|
122
|
+
# invoked and the subscriber will be added or removed from the Array.
|
104
123
|
#
|
105
|
-
# The subscriber
|
106
|
-
#
|
107
|
-
# will be included or excluded, thereby allowing the list to decide. This
|
108
|
-
# default implementation does not include the sending subscriber in the
|
109
|
-
# list of recipients.
|
124
|
+
# The sending subscriber is provided if the list would like to utilize it
|
125
|
+
# in calculating the recipients.
|
110
126
|
#
|
111
127
|
def recipients(subscriber)
|
112
|
-
subscribers
|
128
|
+
subscribers
|
113
129
|
end
|
114
130
|
|
115
131
|
# A list must answer the subscriber who's email address is that of the one
|
data/lib/mlist/mail_list.rb
CHANGED
@@ -43,7 +43,7 @@ module MList
|
|
43
43
|
:subscriber => email.subscriber,
|
44
44
|
:recipients => list.recipients(email.subscriber),
|
45
45
|
:email => MList::Email.new(:source => email.to_s)
|
46
|
-
), :search_parent => false
|
46
|
+
), :search_parent => false, :copy_sender => email.copy_sender
|
47
47
|
end
|
48
48
|
|
49
49
|
# Processes the email received by the MList::Server.
|
@@ -55,7 +55,7 @@ module MList
|
|
55
55
|
:subscriber => subscriber,
|
56
56
|
:recipients => recipients,
|
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
|
@@ -175,7 +175,8 @@ module MList
|
|
175
175
|
|
176
176
|
options = {
|
177
177
|
:search_parent => true,
|
178
|
-
:delivery_time => Time.now
|
178
|
+
:delivery_time => Time.now,
|
179
|
+
:copy_sender => false
|
179
180
|
}.merge(options)
|
180
181
|
|
181
182
|
transaction do
|
@@ -198,15 +199,25 @@ module MList
|
|
198
199
|
message.identifier = outgoing_server.generate_message_id
|
199
200
|
message.created_at = options[:delivery_time]
|
200
201
|
message.subject = clean_subject(message.subject)
|
202
|
+
|
203
|
+
recipient_addresses = message.recipient_addresses
|
204
|
+
sender_address = message.subscriber.email_address
|
205
|
+
if options[:copy_sender]
|
206
|
+
recipient_addresses << sender_address unless recipient_addresses.include?(sender_address)
|
207
|
+
else
|
208
|
+
recipient_addresses.delete(sender_address)
|
209
|
+
end
|
210
|
+
|
201
211
|
returning(message.delivery) do |delivery|
|
202
|
-
delivery.date
|
212
|
+
delivery.date ||= options[:delivery_time]
|
203
213
|
delivery.message_id = message.identifier
|
204
214
|
delivery.mailer = message.mailer
|
205
215
|
delivery.headers = list_headers
|
206
216
|
delivery.subject = list_subject(message.subject)
|
207
217
|
delivery.to = address
|
208
|
-
delivery.
|
209
|
-
delivery.
|
218
|
+
delivery.cc = []
|
219
|
+
delivery.bcc = recipient_addresses
|
220
|
+
delivery.reply_to ||= reply_to_header(message)
|
210
221
|
prepare_list_footer(delivery, message)
|
211
222
|
end
|
212
223
|
end
|
@@ -240,6 +251,14 @@ module MList
|
|
240
251
|
message.parent ? message.parent.thread : threads.build
|
241
252
|
end
|
242
253
|
|
254
|
+
def reply_to_header(message)
|
255
|
+
if list.reply_to_list?
|
256
|
+
"#{label} #{bracket(address)}"
|
257
|
+
else
|
258
|
+
subscriber_name_and_address(message.subscriber)
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
243
262
|
def subject_prefix_regex
|
244
263
|
@subject_prefix_regex ||= Regexp.new(Regexp.escape(subject_prefix) + ' ')
|
245
264
|
end
|
data/lib/mlist/message.rb
CHANGED
@@ -86,6 +86,16 @@ module MList
|
|
86
86
|
end
|
87
87
|
alias_method_chain :parent=, :identifier_capture
|
88
88
|
|
89
|
+
# Answers the recipient email addresses from the MList::List recipient
|
90
|
+
# subscribers, except those that are in the email TO or CC fields as
|
91
|
+
# placed there by the sending MUA. It is assumed that those addresses have
|
92
|
+
# received a copy of the email already, and that by including them here,
|
93
|
+
# we would cause them to receive two copies of the message.
|
94
|
+
#
|
95
|
+
def recipient_addresses
|
96
|
+
@recipients.collect(&:email_address).collect(&:downcase) - email.recipient_addresses
|
97
|
+
end
|
98
|
+
|
89
99
|
# Answers the subject with 'Re:' prefixed. Note that it is the
|
90
100
|
# responsibility of the MList::MailList to perform any processing of the
|
91
101
|
# persisted subject (ie, cleaning up labels, etc).
|
data/lib/mlist/thread.rb
CHANGED
@@ -15,12 +15,12 @@ module MList
|
|
15
15
|
|
16
16
|
def next(message)
|
17
17
|
i = tree_order.index(message)
|
18
|
-
|
18
|
+
tree_order[i + 1] unless messages.size < i
|
19
19
|
end
|
20
20
|
|
21
21
|
def previous(message)
|
22
22
|
i = tree_order.index(message)
|
23
|
-
|
23
|
+
tree_order[i - 1] if i > 0
|
24
24
|
end
|
25
25
|
|
26
26
|
def subject
|
@@ -2,8 +2,15 @@ module MList
|
|
2
2
|
module Util
|
3
3
|
|
4
4
|
class HtmlTextExtraction
|
5
|
+
|
6
|
+
# We need a way to maintain non-breaking spaces. Hpricot will replace
|
7
|
+
# them with ??.chr. We can easily teach it to convert it to a space, but
|
8
|
+
# then we lose the information in the Text node that we need to keep the
|
9
|
+
# space around, since that is what they would see in a view of the HTML.
|
10
|
+
NBSP = '!!!NBSP!!!'
|
11
|
+
|
5
12
|
def initialize(html)
|
6
|
-
@doc = Hpricot(html)
|
13
|
+
@doc = Hpricot(html.gsub(' ', NBSP))
|
7
14
|
end
|
8
15
|
|
9
16
|
def execute
|
@@ -19,7 +26,7 @@ module MList
|
|
19
26
|
end
|
20
27
|
@text << "\n\n--\n#{refs.join("\n")}"
|
21
28
|
end
|
22
|
-
@text
|
29
|
+
@text.gsub(NBSP, ' ')
|
23
30
|
end
|
24
31
|
|
25
32
|
def extract_text_from_node(node)
|
@@ -95,6 +102,12 @@ module MList
|
|
95
102
|
text.to_s.gsub(/\r\n?/, "\n")
|
96
103
|
end
|
97
104
|
|
105
|
+
def subscriber_name_and_address(subscriber)
|
106
|
+
a = subscriber.email_address
|
107
|
+
a = "#{subscriber.display_name} #{bracket(a)}" if subscriber.respond_to?(:display_name)
|
108
|
+
a
|
109
|
+
end
|
110
|
+
|
98
111
|
BRACKETS_RE = /\A<(.*?)>\Z/
|
99
112
|
def bracket(string)
|
100
113
|
string.blank? || string =~ BRACKETS_RE ? string : "<#{string}>"
|
data/rails/init.rb
CHANGED
@@ -1,28 +1,22 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
# attention.
|
22
|
-
#
|
23
|
-
def reload_application_with_plugin_record_support
|
24
|
-
ActiveRecord::Base.send(:subclasses).each(&:delete_observers)
|
25
|
-
reload_application_without_plugin_record_support
|
1
|
+
# Provides the mechinism to support applications that want to observe MList
|
2
|
+
# models.
|
3
|
+
#
|
4
|
+
# ActiveRecord observers are reloaded at each request in development mode.
|
5
|
+
# They will be registered with the MList models each time. Since the MList
|
6
|
+
# models are required once at initialization, there will always only be one
|
7
|
+
# instance of the model class, and therefore, many instances of the observer
|
8
|
+
# class registered with it; all but the most recent are invalid, since they
|
9
|
+
# were undefined when the dispatcher reloaded the application.
|
10
|
+
#
|
11
|
+
# Should we ever have observers in MList, this will likely need more careful
|
12
|
+
# attention.
|
13
|
+
#
|
14
|
+
unless Rails.configuration.cache_classes
|
15
|
+
class << ActiveRecord::Base
|
16
|
+
def instantiate_observers_with_mlist_observers
|
17
|
+
subclasses.each(&:delete_observers)
|
18
|
+
instantiate_observers_without_mlist_observers
|
19
|
+
end
|
20
|
+
alias_method_chain :instantiate_observers, :mlist_observers
|
26
21
|
end
|
27
|
-
alias_method_chain :reload_application, :plugin_record_support
|
28
22
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: aiwilliams-mlist
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Adam Williams
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-
|
12
|
+
date: 2009-08-14 00:00:00 -07:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|
@@ -59,6 +59,7 @@ files:
|
|
59
59
|
- rails/init.rb
|
60
60
|
has_rdoc: false
|
61
61
|
homepage: http://github.com/aiwilliams/mlist
|
62
|
+
licenses:
|
62
63
|
post_install_message:
|
63
64
|
rdoc_options: []
|
64
65
|
|
@@ -79,7 +80,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
79
80
|
requirements: []
|
80
81
|
|
81
82
|
rubyforge_project:
|
82
|
-
rubygems_version: 1.
|
83
|
+
rubygems_version: 1.3.5
|
83
84
|
signing_key:
|
84
85
|
specification_version: 2
|
85
86
|
summary: A Ruby mailing list library designed to be integrated into other applications.
|