73
76
class MailingList(Model):
74
77
"""See `IMailingList`."""
76
id = Int(primary=True)
79
__tablename__ = 'mailinglist'
81
id = Column(Integer, primary_key=True)
78
83
# XXX denotes attributes that should be part of the public interface but
79
84
# are currently missing.
84
_list_id = Unicode(name='list_id')
85
allow_list_posts = Bool()
86
include_rfc2369_headers = Bool()
88
anonymous_list = Bool()
87
list_name = Column(Unicode)
88
mail_host = Column(Unicode)
89
_list_id = Column('list_id', Unicode)
90
allow_list_posts = Column(Boolean)
91
include_rfc2369_headers = Column(Boolean)
92
advertised = Column(Boolean)
93
anonymous_list = Column(Boolean)
89
94
# Attributes not directly modifiable via the web u/i
90
created_at = DateTime()
91
# Attributes which are directly modifiable via the web u/i. The more
92
# complicated attributes are currently stored as pickles, though that
93
# will change as the schema and implementation is developed.
94
next_request_id = Int()
95
next_digest_number = Int()
96
digest_last_sent_at = DateTime()
98
last_post_at = DateTime()
99
# Implicit destination.
100
acceptable_aliases_id = Int()
101
acceptable_alias = Reference(acceptable_aliases_id, 'AcceptableAlias.id')
102
# Attributes which are directly modifiable via the web u/i. The more
103
# complicated attributes are currently stored as pickles, though that
104
# will change as the schema and implementation is developed.
105
accept_these_nonmembers = Pickle() # XXX
106
admin_immed_notify = Bool()
107
admin_notify_mchanges = Bool()
108
administrivia = Bool()
109
archive_policy = Enum(ArchivePolicy)
95
created_at = Column(DateTime)
96
# Attributes which are directly modifiable via the web u/i. The more
97
# complicated attributes are currently stored as pickles, though that
98
# will change as the schema and implementation is developed.
99
next_request_id = Column(Integer)
100
next_digest_number = Column(Integer)
101
digest_last_sent_at = Column(DateTime)
102
volume = Column(Integer)
103
last_post_at = Column(DateTime)
104
# Attributes which are directly modifiable via the web u/i. The more
105
# complicated attributes are currently stored as pickles, though that
106
# will change as the schema and implementation is developed.
107
accept_these_nonmembers = Column(PickleType) # XXX
108
admin_immed_notify = Column(Boolean)
109
admin_notify_mchanges = Column(Boolean)
110
administrivia = Column(Boolean)
111
archive_policy = Column(Enum(ArchivePolicy))
110
112
# Automatic responses.
111
autoresponse_grace_period = TimeDelta()
112
autorespond_owner = Enum(ResponseAction)
113
autoresponse_owner_text = Unicode()
114
autorespond_postings = Enum(ResponseAction)
115
autoresponse_postings_text = Unicode()
116
autorespond_requests = Enum(ResponseAction)
117
autoresponse_request_text = Unicode()
113
autoresponse_grace_period = Column(Interval)
114
autorespond_owner = Column(Enum(ResponseAction))
115
autoresponse_owner_text = Column(Unicode)
116
autorespond_postings = Column(Enum(ResponseAction))
117
autoresponse_postings_text = Column(Unicode)
118
autorespond_requests = Column(Enum(ResponseAction))
119
autoresponse_request_text = Column(Unicode)
118
120
# Content filters.
119
filter_action = Enum(FilterAction)
120
filter_content = Bool()
121
collapse_alternatives = Bool()
122
convert_html_to_plaintext = Bool()
121
filter_action = Column(Enum(FilterAction))
122
filter_content = Column(Boolean)
123
collapse_alternatives = Column(Boolean)
124
convert_html_to_plaintext = Column(Boolean)
124
bounce_info_stale_after = TimeDelta() # XXX
125
bounce_matching_headers = Unicode() # XXX
126
bounce_notify_owner_on_disable = Bool() # XXX
127
bounce_notify_owner_on_removal = Bool() # XXX
128
bounce_score_threshold = Int() # XXX
129
bounce_you_are_disabled_warnings = Int() # XXX
130
bounce_you_are_disabled_warnings_interval = TimeDelta() # XXX
131
forward_unrecognized_bounces_to = Enum(UnrecognizedBounceDisposition)
132
process_bounces = Bool()
126
bounce_info_stale_after = Column(Interval) # XXX
127
bounce_matching_headers = Column(Unicode) # XXX
128
bounce_notify_owner_on_disable = Column(Boolean) # XXX
129
bounce_notify_owner_on_removal = Column(Boolean) # XXX
130
bounce_score_threshold = Column(Integer) # XXX
131
bounce_you_are_disabled_warnings = Column(Integer) # XXX
132
bounce_you_are_disabled_warnings_interval = Column(Interval) # XXX
133
forward_unrecognized_bounces_to = Column(
134
Enum(UnrecognizedBounceDisposition))
135
process_bounces = Column(Boolean)
134
default_member_action = Enum(Action)
135
default_nonmember_action = Enum(Action)
136
description = Unicode()
137
digest_footer_uri = Unicode()
138
digest_header_uri = Unicode()
139
digest_is_default = Bool()
140
digest_send_periodic = Bool()
141
digest_size_threshold = Float()
142
digest_volume_frequency = Enum(DigestFrequency)
144
discard_these_nonmembers = Pickle()
146
encode_ascii_prefixes = Bool()
147
first_strip_reply_to = Bool()
148
footer_uri = Unicode()
149
forward_auto_discards = Bool()
150
gateway_to_mail = Bool()
151
gateway_to_news = Bool()
152
goodbye_message_uri = Unicode()
153
header_matches = Pickle()
154
header_uri = Unicode()
155
hold_these_nonmembers = Pickle()
157
linked_newsgroup = Unicode()
158
max_days_to_hold = Int()
159
max_message_size = Int()
160
max_num_recipients = Int()
161
member_moderation_notice = Unicode()
162
mime_is_default_digest = Bool()
137
default_member_action = Column(Enum(Action))
138
default_nonmember_action = Column(Enum(Action))
139
description = Column(Unicode)
140
digest_footer_uri = Column(Unicode)
141
digest_header_uri = Column(Unicode)
142
digest_is_default = Column(Boolean)
143
digest_send_periodic = Column(Boolean)
144
digest_size_threshold = Column(Float)
145
digest_volume_frequency = Column(Enum(DigestFrequency))
146
digestable = Column(Boolean)
147
discard_these_nonmembers = Column(PickleType)
148
emergency = Column(Boolean)
149
encode_ascii_prefixes = Column(Boolean)
150
first_strip_reply_to = Column(Boolean)
151
footer_uri = Column(Unicode)
152
forward_auto_discards = Column(Boolean)
153
gateway_to_mail = Column(Boolean)
154
gateway_to_news = Column(Boolean)
155
goodbye_message_uri = Column(Unicode)
156
header_matches = Column(PickleType)
157
header_uri = Column(Unicode)
158
hold_these_nonmembers = Column(PickleType)
159
info = Column(Unicode)
160
linked_newsgroup = Column(Unicode)
161
max_days_to_hold = Column(Integer)
162
max_message_size = Column(Integer)
163
max_num_recipients = Column(Integer)
164
member_moderation_notice = Column(Unicode)
165
mime_is_default_digest = Column(Boolean)
163
166
# FIXME: There should be no moderator_password
164
moderator_password = RawStr()
165
newsgroup_moderation = Enum(NewsgroupModeration)
166
nntp_prefix_subject_too = Bool()
167
nondigestable = Bool()
168
nonmember_rejection_notice = Unicode()
169
obscure_addresses = Bool()
170
owner_chain = Unicode()
171
owner_pipeline = Unicode()
172
personalize = Enum(Personalization)
174
posting_chain = Unicode()
175
posting_pipeline = Unicode()
176
_preferred_language = Unicode(name='preferred_language')
177
display_name = Unicode()
178
reject_these_nonmembers = Pickle()
179
reply_goes_to_list = Enum(ReplyToMunging)
180
reply_to_address = Unicode()
181
require_explicit_destination = Bool()
182
respond_to_post_requests = Bool()
183
scrub_nondigest = Bool()
184
send_goodbye_message = Bool()
185
send_welcome_message = Bool()
186
subject_prefix = Unicode()
188
topics_bodylines_limit = Int()
189
topics_enabled = Bool()
190
welcome_message_uri = Unicode()
167
moderator_password = Column(LargeBinary) # TODO : was RawStr()
168
newsgroup_moderation = Column(Enum(NewsgroupModeration))
169
nntp_prefix_subject_too = Column(Boolean)
170
nondigestable = Column(Boolean)
171
nonmember_rejection_notice = Column(Unicode)
172
obscure_addresses = Column(Boolean)
173
owner_chain = Column(Unicode)
174
owner_pipeline = Column(Unicode)
175
personalize = Column(Enum(Personalization))
176
post_id = Column(Integer)
177
posting_chain = Column(Unicode)
178
posting_pipeline = Column(Unicode)
179
_preferred_language = Column('preferred_language', Unicode)
180
display_name = Column(Unicode)
181
reject_these_nonmembers = Column(PickleType)
182
reply_goes_to_list = Column(Enum(ReplyToMunging))
183
reply_to_address = Column(Unicode)
184
require_explicit_destination = Column(Boolean)
185
respond_to_post_requests = Column(Boolean)
186
scrub_nondigest = Column(Boolean)
187
send_goodbye_message = Column(Boolean)
188
send_welcome_message = Column(Boolean)
189
subject_prefix = Column(Unicode)
190
topics = Column(PickleType)
191
topics_bodylines_limit = Column(Integer)
192
topics_enabled = Column(Boolean)
193
welcome_message_uri = Column(Unicode)
192
195
def __init__(self, fqdn_listname):
193
196
super(MailingList, self).__init__()
323
334
except AttributeError:
324
335
self._preferred_language = language
326
def send_one_last_digest_to(self, address, delivery_mode):
338
def send_one_last_digest_to(self, store, address, delivery_mode):
327
339
"""See `IMailingList`."""
328
340
digest = OneLastDigest(self, address, delivery_mode)
329
Store.of(self).add(digest)
332
def last_digest_recipients(self):
345
def last_digest_recipients(self, store):
333
346
"""See `IMailingList`."""
334
results = Store.of(self).find(
347
results = store.query(OneLastDigest).filter(
336
348
OneLastDigest.mailing_list == self)
337
349
recipients = [(digest.address, digest.delivery_mode)
338
350
for digest in results]
340
352
return recipients
343
def filter_types(self):
356
def filter_types(self, store):
344
357
"""See `IMailingList`."""
345
results = Store.of(self).find(
347
And(ContentFilter.mailing_list == self,
348
ContentFilter.filter_type == FilterType.filter_mime))
358
results = store.query(ContentFilter).filter(
359
ContentFilter.mailing_list == self,
360
ContentFilter.filter_type == FilterType.filter_mime)
349
361
for content_filter in results:
350
362
yield content_filter.filter_pattern
352
364
@filter_types.setter
353
def filter_types(self, sequence):
366
def filter_types(self, store, sequence):
354
367
"""See `IMailingList`."""
355
368
# First, delete all existing MIME type filter patterns.
356
store = Store.of(self)
357
results = store.find(
359
And(ContentFilter.mailing_list == self,
360
ContentFilter.filter_type == FilterType.filter_mime))
369
results = store.query(ContentFilter).filter(
370
ContentFilter.mailing_list == self,
371
ContentFilter.filter_type == FilterType.filter_mime)
362
373
# Now add all the new filter types.
363
374
for mime_type in sequence:
364
375
content_filter = ContentFilter(
577
591
@implementer(IListArchiverSet)
578
592
class ListArchiverSet:
579
def __init__(self, mailing_list):
594
def __init__(self, store, mailing_list):
580
595
self._mailing_list = mailing_list
581
596
system_archivers = {}
582
597
for archiver in config.archivers:
583
598
system_archivers[archiver.name] = archiver
584
599
# Add any system enabled archivers which aren't already associated
585
600
# with the mailing list.
586
store = Store.of(self._mailing_list)
587
601
for archiver_name in system_archivers:
590
And(ListArchiver.mailing_list == mailing_list,
591
ListArchiver.name == archiver_name)).one()
602
exists = store.query(ListArchiver).filter(
603
ListArchiver.mailing_list == mailing_list,
604
ListArchiver.name == archiver_name).first()
592
605
if exists is None:
593
606
store.add(ListArchiver(mailing_list, archiver_name,
594
607
system_archivers[archiver_name]))
598
entries = Store.of(self._mailing_list).find(
599
ListArchiver, ListArchiver.mailing_list == self._mailing_list)
611
def archivers(self, store):
612
entries = store.query(ListArchiver).filter(
613
ListArchiver.mailing_list == self._mailing_list)
600
614
for entry in entries:
603
def get(self, archiver_name):
604
return Store.of(self._mailing_list).find(
606
And(ListArchiver.mailing_list == self._mailing_list,
607
ListArchiver.name == archiver_name)).one()
618
def get(self, store, archiver_name):
619
return store.query(ListArchiver).filter(
620
ListArchiver.mailing_list == self._mailing_list,
621
ListArchiver.name == archiver_name).first()