thread merge
[mailman.git] / mailman / database / mailinglist.py
blob0b80ca0b58415e63cd3f665412964156070672e4
1 # Copyright (C) 2006-2008 by the Free Software Foundation, Inc.
3 # This program is free software; you can redistribute it and/or
4 # modify it under the terms of the GNU General Public License
5 # as published by the Free Software Foundation; either version 2
6 # of the License, or (at your option) any later version.
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
13 # You should have received a copy of the GNU General Public License
14 # along with this program; if not, write to the Free Software
15 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
16 # USA.
18 import os
19 import string
21 from storm.locals import *
22 from zope.interface import implements
24 from mailman.Utils import fqdn_listname, makedirs, split_listname
25 from mailman.configuration import config
26 from mailman.database import roster
27 from mailman.database.model import Model
28 from mailman.database.types import Enum
29 from mailman.interfaces import IMailingList, Personalization
32 SPACE = ' '
33 UNDERSCORE = '_'
37 class MailingList(Model):
38 implements(IMailingList)
40 id = Int(primary=True)
42 # List identity
43 list_name = Unicode()
44 host_name = Unicode()
45 # Attributes not directly modifiable via the web u/i
46 created_at = DateTime()
47 web_page_url = Unicode()
48 admin_member_chunksize = Int()
49 hold_and_cmd_autoresponses = Pickle()
50 # Attributes which are directly modifiable via the web u/i. The more
51 # complicated attributes are currently stored as pickles, though that
52 # will change as the schema and implementation is developed.
53 next_request_id = Int()
54 next_digest_number = Int()
55 admin_responses = Pickle()
56 postings_responses = Pickle()
57 request_responses = Pickle()
58 digest_last_sent_at = Float()
59 one_last_digest = Pickle()
60 volume = Int()
61 last_post_time = DateTime()
62 # Attributes which are directly modifiable via the web u/i. The more
63 # complicated attributes are currently stored as pickles, though that
64 # will change as the schema and implementation is developed.
65 accept_these_nonmembers = Pickle()
66 acceptable_aliases = Pickle()
67 admin_immed_notify = Bool()
68 admin_notify_mchanges = Bool()
69 administrivia = Bool()
70 advertised = Bool()
71 anonymous_list = Bool()
72 archive = Bool()
73 archive_private = Bool()
74 archive_volume_frequency = Int()
75 autorespond_admin = Bool()
76 autorespond_postings = Bool()
77 autorespond_requests = Int()
78 autoresponse_admin_text = Unicode()
79 autoresponse_graceperiod = TimeDelta()
80 autoresponse_postings_text = Unicode()
81 autoresponse_request_text = Unicode()
82 ban_list = Pickle()
83 bounce_info_stale_after = TimeDelta()
84 bounce_matching_headers = Unicode()
85 bounce_notify_owner_on_disable = Bool()
86 bounce_notify_owner_on_removal = Bool()
87 bounce_processing = Bool()
88 bounce_score_threshold = Int()
89 bounce_unrecognized_goes_to_list_owner = Bool()
90 bounce_you_are_disabled_warnings = Int()
91 bounce_you_are_disabled_warnings_interval = TimeDelta()
92 collapse_alternatives = Bool()
93 convert_html_to_plaintext = Bool()
94 default_member_moderation = Bool()
95 description = Unicode()
96 digest_footer = Unicode()
97 digest_header = Unicode()
98 digest_is_default = Bool()
99 digest_send_periodic = Bool()
100 digest_size_threshold = Int()
101 digest_volume_frequency = Int()
102 digestable = Bool()
103 discard_these_nonmembers = Pickle()
104 emergency = Bool()
105 encode_ascii_prefixes = Bool()
106 filter_action = Int()
107 filter_content = Bool()
108 filter_filename_extensions = Pickle()
109 filter_mime_types = Pickle()
110 first_strip_reply_to = Bool()
111 forward_auto_discards = Bool()
112 gateway_to_mail = Bool()
113 gateway_to_news = Bool()
114 generic_nonmember_action = Int()
115 goodbye_msg = Unicode()
116 header_matches = Pickle()
117 hold_these_nonmembers = Pickle()
118 include_list_post_header = Bool()
119 include_rfc2369_headers = Bool()
120 info = Unicode()
121 linked_newsgroup = Unicode()
122 max_days_to_hold = Int()
123 max_message_size = Int()
124 max_num_recipients = Int()
125 member_moderation_action = Enum()
126 member_moderation_notice = Unicode()
127 mime_is_default_digest = Bool()
128 moderator_password = Unicode()
129 msg_footer = Unicode()
130 msg_header = Unicode()
131 new_member_options = Int()
132 news_moderation = Enum()
133 news_prefix_subject_too = Bool()
134 nntp_host = Unicode()
135 nondigestable = Bool()
136 nonmember_rejection_notice = Unicode()
137 obscure_addresses = Bool()
138 pass_filename_extensions = Pickle()
139 pass_mime_types = Pickle()
140 personalize = Enum()
141 pipeline = Unicode()
142 post_id = Int()
143 preferred_language = Unicode()
144 private_roster = Bool()
145 real_name = Unicode()
146 reject_these_nonmembers = Pickle()
147 reply_goes_to_list = Enum()
148 reply_to_address = Unicode()
149 require_explicit_destination = Bool()
150 respond_to_post_requests = Bool()
151 scrub_nondigest = Bool()
152 send_goodbye_msg = Bool()
153 send_reminders = Bool()
154 send_welcome_msg = Bool()
155 start_chain = Unicode()
156 subject_prefix = Unicode()
157 subscribe_auto_approval = Pickle()
158 subscribe_policy = Int()
159 topics = Pickle()
160 topics_bodylines_limit = Int()
161 topics_enabled = Bool()
162 unsubscribe_policy = Int()
163 welcome_msg = Unicode()
165 def __init__(self, fqdn_listname):
166 super(MailingList, self).__init__()
167 listname, hostname = split_listname(fqdn_listname)
168 self.list_name = listname
169 self.host_name = hostname
170 # For the pending database
171 self.next_request_id = 1
172 self._restore()
173 # Max autoresponses per day. A mapping between addresses and a
174 # 2-tuple of the date of the last autoresponse and the number of
175 # autoresponses sent on that date.
176 self.hold_and_cmd_autoresponses = {}
177 self.personalization = Personalization.none
178 self.real_name = string.capwords(
179 SPACE.join(listname.split(UNDERSCORE)))
180 makedirs(self.data_path)
182 # XXX FIXME
183 def _restore(self):
184 self.owners = roster.OwnerRoster(self)
185 self.moderators = roster.ModeratorRoster(self)
186 self.administrators = roster.AdministratorRoster(self)
187 self.members = roster.MemberRoster(self)
188 self.regular_members = roster.RegularMemberRoster(self)
189 self.digest_members = roster.DigestMemberRoster(self)
190 self.subscribers = roster.Subscribers(self)
192 @property
193 def fqdn_listname(self):
194 """See `IMailingList`."""
195 return fqdn_listname(self.list_name, self.host_name)
197 @property
198 def web_host(self):
199 """See `IMailingList`."""
200 return config.domains[self.host_name]
202 def script_url(self, target, context=None):
203 """See `IMailingList`."""
204 # XXX Handle the case for when context is not None; those would be
205 # relative URLs.
206 return self.web_page_url + target + '/' + self.fqdn_listname
208 @property
209 def data_path(self):
210 """See `IMailingList`."""
211 return os.path.join(config.LIST_DATA_DIR, self.fqdn_listname)
213 # IMailingListAddresses
215 @property
216 def posting_address(self):
217 return self.fqdn_listname
219 @property
220 def no_reply_address(self):
221 return '%s@%s' % (config.NO_REPLY_ADDRESS, self.host_name)
223 @property
224 def owner_address(self):
225 return '%s-owner@%s' % (self.list_name, self.host_name)
227 @property
228 def request_address(self):
229 return '%s-request@%s' % (self.list_name, self.host_name)
231 @property
232 def bounces_address(self):
233 return '%s-bounces@%s' % (self.list_name, self.host_name)
235 @property
236 def join_address(self):
237 return '%s-join@%s' % (self.list_name, self.host_name)
239 @property
240 def leave_address(self):
241 return '%s-leave@%s' % (self.list_name, self.host_name)
243 @property
244 def subscribe_address(self):
245 return '%s-subscribe@%s' % (self.list_name, self.host_name)
247 @property
248 def unsubscribe_address(self):
249 return '%s-unsubscribe@%s' % (self.list_name, self.host_name)
251 def confirm_address(self, cookie):
252 template = string.Template(config.VERP_CONFIRM_FORMAT)
253 local_part = template.safe_substitute(
254 address = '%s-confirm' % self.list_name,
255 cookie = cookie)
256 return '%s@%s' % (local_part, self.host_name)
258 def __repr__(self):
259 return '<mailing list "%s" at %#x>' % (self.fqdn_listname, id(self))