aiwilliams-mlist 0.1.6 → 0.1.7
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/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.
|