1 # Copyright (C) 1998-2007 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,
18 """Routines for presentation of list-specific HTML text."""
23 from Mailman
import Defaults
24 from Mailman
import MemberAdaptor
25 from Mailman
import Utils
26 from Mailman
.configuration
import config
27 from Mailman
.htmlformat
import *
28 from Mailman
.i18n
import _
39 def GetMailmanFooter(self
):
40 ownertext
= COMMASPACE
.join([Utils
.ObscureEmail(a
, 1)
42 # Remove the .Format() when htmlformat conversion is done.
43 realname
= self
.real_name
44 hostname
= self
.host_name
45 listinfo_link
= Link(self
.GetScriptURL('listinfo'),
47 owner_link
= Link('mailto:' + self
.GetOwnerEmail(), ownertext
).Format()
48 innertext
= _('%(listinfo_link)s list run by %(owner_link)s')
55 Link(self
.GetScriptURL('admin'),
56 _('%(realname)s administrative interface')),
57 _(' (requires authorization)'),
59 Link(Utils
.ScriptURL('listinfo'),
60 _('Overview of all %(hostname)s mailing lists')),
61 '<p>', MailmanLogo()))).Format()
63 def FormatUsers(self
, digest
, lang
=None):
65 lang
= self
.preferred_language
66 conceal_sub
= Defaults
.ConcealSubscription
69 digestmembers
= self
.getDigestMemberKeys()
70 for dm
in digestmembers
:
71 if not self
.getMemberOption(dm
, conceal_sub
):
73 num_concealed
= len(digestmembers
) - len(people
)
75 members
= self
.getRegularMemberKeys()
77 if not self
.getMemberOption(m
, conceal_sub
):
79 num_concealed
= len(members
) - len(people
)
80 if num_concealed
== 1:
81 concealed
= _('<em>(1 private member not shown)</em>')
82 elif num_concealed
> 1:
84 '<em>(%(num_concealed)d private members not shown)</em>')
89 obscure
= self
.obscure_addresses
91 id = Utils
.ObscureEmail(person
)
92 url
= self
.GetOptionsURL(person
, obscure
=obscure
)
94 showing
= Utils
.ObscureEmail(person
, for_text
=1)
97 got
= Link(url
, showing
)
98 if self
.getDeliveryStatus(person
) <> MemberAdaptor
.ENABLED
:
99 got
= Italic('(', got
, ')')
101 # Just return the .Format() so this works until I finish
102 # converting everything to htmlformat...
103 return concealed
+ UnorderedList(*tuple(items
)).Format()
105 def FormatOptionButton(self
, option
, value
, user
):
106 if option
== Defaults
.DisableDelivery
:
107 optval
= self
.getDeliveryStatus(user
) <> MemberAdaptor
.ENABLED
109 optval
= self
.getMemberOption(user
, option
)
115 Defaults
.DontReceiveOwnPosts
: 'dontreceive',
116 Defaults
.DisableDelivery
: 'disablemail',
117 Defaults
.DisableMime
: 'mime',
118 Defaults
.AcknowledgePosts
: 'ackposts',
119 Defaults
.Digests
: 'digest',
120 Defaults
.ConcealSubscription
: 'conceal',
121 Defaults
.SuppressPasswordReminder
: 'remind',
122 Defaults
.ReceiveNonmatchingTopics
: 'rcvtopic',
123 Defaults
.DontReceiveDuplicates
: 'nodupes',
125 return '<input type=radio name="%s" value="%d"%s>' % (
126 name
, value
, checked
)
128 def FormatDigestButton(self
):
129 if self
.digest_is_default
:
133 return '<input type=radio name="digest" value="1"%s>' % checked
135 def FormatDisabledNotice(self
, user
):
136 status
= self
.getDeliveryStatus(user
)
138 info
= self
.getBounceInfo(user
)
139 if status
== MemberAdaptor
.BYUSER
:
140 reason
= _('; it was disabled by you')
141 elif status
== MemberAdaptor
.BYADMIN
:
142 reason
= _('; it was disabled by the list administrator')
143 elif status
== MemberAdaptor
.BYBOUNCE
:
144 date
= time
.strftime('%d-%b-%Y',
145 time
.localtime(Utils
.midnight(info
.date
)))
146 reason
= _('''; it was disabled due to excessive bounces. The
147 last bounce was received on %(date)s''')
148 elif status
== MemberAdaptor
.UNKNOWN
:
149 reason
= _('; it was disabled for unknown reasons')
151 note
= FontSize('+1', _(
152 'Note: your list delivery is currently disabled%(reason)s.'
154 link
= Link('#disable', _('Mail delivery')).Format()
155 mailto
= Link('mailto:' + self
.GetOwnerEmail(),
156 _('the list administrator')).Format()
157 return _('''<p>%(note)s
159 <p>You may have disabled list delivery intentionally,
160 or it may have been triggered by bounces from your email
161 address. In either case, to re-enable delivery, change the
162 %(link)s option below. Contact %(mailto)s if you have any
163 questions or need assistance.''')
164 elif info
and info
.score
> 0:
165 # Provide information about their current bounce score. We know
166 # their membership is currently enabled.
168 total
= self
.bounce_score_threshold
169 return _('''<p>We have received some recent bounces from your
170 address. Your current <em>bounce score</em> is %(score)s out of a
171 maximum of %(total)s. Please double check that your subscribed
172 address is correct and that there are no problems with delivery to
173 this address. Your bounce score will be automatically reset if
174 the problems are corrected soon.''')
178 def FormatUmbrellaNotice(self
, user
, type):
179 addr
= self
.GetMemberAdminEmail(user
)
180 if self
.umbrella_list
:
181 return _("(Note - you are subscribing to a list of mailing lists, "
182 "so the %(type)s notice will be sent to the admin address"
183 " for your membership, %(addr)s.)<p>")
187 def FormatSubscriptionMsg(self
):
190 if self
.subscribe_policy
== 1:
191 msg
+= _('''You will be sent email requesting confirmation, to
192 prevent others from gratuitously subscribing you.''')
193 elif self
.subscribe_policy
== 2:
194 msg
+= _("""This is a closed list, which means your subscription
195 will be held for approval. You will be notified of the list
196 moderator's decision by email.""")
198 elif self
.subscribe_policy
== 3:
199 msg
+= _("""You will be sent email requesting confirmation, to
200 prevent others from gratuitously subscribing you. Once
201 confirmation is received, your request will be held for approval
202 by the list moderator. You will be notified of the moderator's
203 decision by email.""")
207 if self
.private_roster
== 1:
208 msg
+= _('''This is %(also)sa private list, which means that the
209 list of members is not available to non-members.''')
210 elif self
.private_roster
:
211 msg
+= _('''This is %(also)sa hidden list, which means that the
212 list of members is available only to the list administrator.''')
214 msg
+= _('''This is %(also)sa public list, which means that the
215 list of members list is available to everyone.''')
216 if self
.obscure_addresses
:
217 msg
+= _(''' (but we obscure the addresses so they are not
218 easily recognizable by spammers).''')
220 if self
.umbrella_list
:
221 sfx
= self
.umbrella_member_suffix
222 msg
+= _("""<p>(Note that this is an umbrella list, intended to
223 have only other mailing lists as members. Among other things,
224 this means that your confirmation request will be sent to the
225 `%(sfx)s' account for your address.)""")
228 def FormatUndigestButton(self
):
229 if self
.digest_is_default
:
233 return '<input type=radio name="digest" value="0"%s>' % checked
235 def FormatMimeDigestsButton(self
):
236 if self
.mime_is_default_digest
:
240 return '<input type=radio name="mime" value="1"%s>' % checked
242 def FormatPlainDigestsButton(self
):
243 if self
.mime_is_default_digest
:
247 return '<input type=radio name="plain" value="1"%s>' % checked
249 def FormatEditingOption(self
, lang
):
250 if self
.private_roster
== 0:
251 either
= _('<b><i>either</i></b> ')
254 realname
= self
.real_name
256 text
= (_('''To unsubscribe from %(realname)s, get a password reminder,
257 or change your subscription options %(either)senter your subscription
260 + TextBox('email', size
=30).Format()
262 + SubmitButton('UserOptions',
263 _('Unsubscribe or edit options')).Format()
264 + Hidden('language', lang
).Format()
266 if self
.private_roster
== 0:
267 text
+= _('''<p>... <b><i>or</i></b> select your entry from
268 the subscribers list (see above).''')
269 text
+= _(''' If you leave the field blank, you will be prompted for
270 your email address''')
273 def RestrictedListMessage(self
, which
, restriction
):
276 elif restriction
== 1:
278 '''(<i>%(which)s is only available to the list
281 return _('''(<i>%(which)s is only available to the list
282 administrator.</i>)''')
284 def FormatRosterOptionForUser(self
, lang
):
285 return self
.RosterOption(lang
).Format()
287 def RosterOption(self
, lang
):
288 container
= Container()
289 container
.AddItem(Hidden('language', lang
))
290 if not self
.private_roster
:
291 container
.AddItem(_("Click here for the list of ")
293 + _(" subscribers: "))
294 container
.AddItem(SubmitButton('SubscriberRoster',
295 _("Visit Subscriber list")))
297 if self
.private_roster
== 1:
301 only
= _('the list administrator')
302 whom
= _('Admin address:')
303 # Solicit the user and password.
305 self
.RestrictedListMessage(_('The subscribers list'),
307 + _(" <p>Enter your ")
309 + _(" and password to visit"
310 " the subscribers list: <p><center> ")
313 container
.AddItem(self
.FormatBox('roster-email'))
314 container
.AddItem(_("Password: ")
315 + self
.FormatSecureBox('roster-pw')
317 container
.AddItem(SubmitButton('SubscriberRoster',
318 _('Visit Subscriber List')))
319 container
.AddItem("</center>")
322 def FormatFormStart(self
, name
, extra
=''):
323 base_url
= self
.GetScriptURL(name
)
325 full_url
= "%s/%s" % (base_url
, extra
)
328 return ('<FORM Method=POST ACTION="%s">' % full_url
)
330 def FormatArchiveAnchor(self
):
331 return '<a href="%s">' % self
.GetBaseArchiveURL()
333 def FormatFormEnd(self
):
336 def FormatBox(self
, name
, size
=20, value
=''):
337 return '<INPUT type="Text" name="%s" size="%d" value="%s">' % (
340 def FormatSecureBox(self
, name
):
341 return '<INPUT type="Password" name="%s" size="15">' % name
343 def FormatButton(self
, name
, text
='Submit'):
344 return '<INPUT type="Submit" name="%s" value="%s">' % (name
, text
)
346 def FormatReminder(self
, lang
):
347 if self
.send_reminders
:
348 return _('Once a month, your password will be emailed to you as'
352 def ParseTags(self
, template
, replacements
, lang
=None):
356 charset
= Utils
.GetCharSet(lang
)
357 text
= Utils
.maketext(template
, raw
=1, lang
=lang
, mlist
=self
)
358 parts
= re
.split('(</?[Mm][Mm]-[^>]*>)', text
)
360 while i
< len(parts
):
361 tag
= parts
[i
].lower()
362 if replacements
.has_key(tag
):
363 repl
= replacements
[tag
]
364 if isinstance(repl
, str):
365 repl
= unicode(repl
, charset
, 'replace')
370 return EMPTYSTRING
.join(parts
)
372 # This needs to wait until after the list is inited, so let's build it
373 # when it's needed only.
374 def GetStandardReplacements(self
, lang
=None):
375 dmember_len
= len(self
.getDigestMemberKeys())
376 member_len
= len(self
.getRegularMemberKeys())
377 # If only one language is enabled for this mailing list, omit the
378 # language choice buttons.
379 if len(self
.language_codes
) == 1:
381 config
.languages
.get_description(self
.preferred_language
))
383 listlangs
= self
.GetLangSelectBox(lang
).Format()
385 '<mm-mailman-footer>' : self
.GetMailmanFooter(),
386 '<mm-list-name>' : self
.real_name
,
387 '<mm-email-user>' : self
._internal
_name
,
388 '<mm-list-description>' : self
.description
,
389 '<mm-list-info>' : BR
.join(self
.info
.split(NL
)),
390 '<mm-form-end>' : self
.FormatFormEnd(),
391 '<mm-archive>' : self
.FormatArchiveAnchor(),
392 '</mm-archive>' : '</a>',
393 '<mm-list-subscription-msg>' : self
.FormatSubscriptionMsg(),
394 '<mm-restricted-list-message>' : \
395 self
.RestrictedListMessage(_('The current archive'),
396 self
.archive_private
),
397 '<mm-num-reg-users>' : `member_len`
,
398 '<mm-num-digesters>' : `dmember_len`
,
399 '<mm-num-members>' : (`member_len
+ dmember_len`
),
400 '<mm-posting-addr>' : '%s' % self
.GetListEmail(),
401 '<mm-request-addr>' : '%s' % self
.GetRequestEmail(),
402 '<mm-owner>' : self
.GetOwnerEmail(),
403 '<mm-reminder>' : self
.FormatReminder(self
.preferred_language
),
404 '<mm-host>' : self
.host_name
,
405 '<mm-list-langs>' : listlangs
,
407 if config
.IMAGE_LOGOS
:
408 d
['<mm-favicon>'] = config
.IMAGE_LOGOS
+ config
.SHORTCUT_ICON
411 def GetAllReplacements(self
, lang
=None):
413 returns standard replaces plus formatted user lists in
414 a dict just like GetStandardReplacements.
417 lang
= self
.preferred_language
418 d
= self
.GetStandardReplacements(lang
)
419 d
.update({"<mm-regular-users>": self
.FormatUsers(0, lang
),
420 "<mm-digest-users>": self
.FormatUsers(1, lang
)})
423 def GetLangSelectBox(self
, lang
=None, varname
='language'):
425 lang
= self
.preferred_language
426 # Figure out the available languages
427 values
= self
.language_codes
428 legend
= [config
.languages
.get_description(code
) for code
in values
]
430 selected
= values
.index(lang
)
433 selected
= values
.index(self
.preferred_language
)
435 selected
= config
.DEFAULT_SERVER_LANGUAGE
437 return SelectOptions(varname
, values
, legend
, selected
)