mailman  2.1.39
About: Mailman 2 - The GNU Mailing List Management System.
  Fossies Dox: mailman-2.1.39.tgz  ("unofficial" and yet experimental doxygen-generated source code documentation)  

HTMLFormatter.py
Go to the documentation of this file.
1# Copyright (C) 1998-2018 by the Free Software Foundation, Inc.
2#
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.
7#
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.
12#
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.
17
18
19"""Routines for presentation of list-specific HTML text."""
20
21import time
22import re
23
24from Mailman import mm_cfg
25from Mailman import Utils
26from Mailman import MemberAdaptor
27from Mailman.htmlformat import *
28
29from Mailman.i18n import _
30
31from Mailman.CSRFcheck import csrf_token
32
33
34EMPTYSTRING = ''
35BR = '<br>'
36NL = '\n'
37COMMASPACE = ', '
38
39
40
43 ownertext = Utils.ObscureEmail(self.GetOwnerEmail(), 1)
44 # Remove the .Format() when htmlformat conversion is done.
45 realname = self.real_name
46 hostname = self.host_name
47 listinfo_link = Link(self.GetScriptURL('listinfo'), realname).Format()
48 owner_link = Link('mailto:' + self.GetOwnerEmail(), ownertext).Format()
49 innertext = _('%(listinfo_link)s list run by %(owner_link)s')
50 return Container(
51 '<hr>',
52 Address(
54 innertext,
55 '<br>',
56 Link(self.GetScriptURL('admin'),
57 _('%(realname)s administrative interface')),
58 _(' (requires authorization)'),
59 '<br>',
60 Link(Utils.ScriptURL('listinfo'),
61 _('Overview of all %(hostname)s mailing lists')),
62 '<p>', MailmanLogo()))).Format()
63
64 def FormatUsers(self, digest, lang=None, list_hidden=False):
65 if lang is None:
66 lang = self.preferred_language
67 conceal_sub = mm_cfg.ConcealSubscription
68 people = []
69 if digest:
70 members = self.getDigestMemberKeys()
71 else:
72 members = self.getRegularMemberKeys()
73 for m in members:
74 if list_hidden or not self.getMemberOption(m, conceal_sub):
75 people.append(m)
76 num_concealed = len(members) - len(people)
77 if num_concealed == 1:
78 concealed = _('<em>(1 private member not shown)</em>')
79 elif num_concealed > 1:
80 concealed = _(
81 '<em>(%(num_concealed)d private members not shown)</em>')
82 else:
83 concealed = ''
84 items = []
85 people.sort()
86 obscure = self.obscure_addresses
87 for person in people:
88 id = Utils.ObscureEmail(person)
89 url = self.GetOptionsURL(person, obscure=obscure)
90 person = self.getMemberCPAddress(person)
91 if obscure:
92 showing = Utils.ObscureEmail(person, for_text=1)
93 else:
94 showing = person
95 realname = Utils.uncanonstr(self.getMemberName(person), lang)
96 if realname and mm_cfg.ROSTER_DISPLAY_REALNAME:
97 showing += " (%s)" % Utils.websafe(realname)
98 got = Link(url, showing)
99 if self.getDeliveryStatus(person) <> MemberAdaptor.ENABLED:
100 got = Italic('(', got, ')')
101 items.append(got)
102 # Just return the .Format() so this works until I finish
103 # converting everything to htmlformat...
104 return concealed + UnorderedList(*tuple(items)).Format()
105
106 def FormatOptionButton(self, option, value, user):
107 if option == mm_cfg.DisableDelivery:
108 optval = self.getDeliveryStatus(user) <> MemberAdaptor.ENABLED
109 else:
110 optval = self.getMemberOption(user, option)
111 if optval == value:
112 checked = ' CHECKED'
113 else:
114 checked = ''
115 name = {mm_cfg.DontReceiveOwnPosts : 'dontreceive',
116 mm_cfg.DisableDelivery : 'disablemail',
117 mm_cfg.DisableMime : 'mime',
118 mm_cfg.AcknowledgePosts : 'ackposts',
119 mm_cfg.Digests : 'digest',
120 mm_cfg.ConcealSubscription : 'conceal',
121 mm_cfg.SuppressPasswordReminder : 'remind',
122 mm_cfg.ReceiveNonmatchingTopics : 'rcvtopic',
123 mm_cfg.DontReceiveDuplicates : 'nodupes',
124 }[option]
125 return '<input type=radio name="%s" value="%d"%s>' % (
126 name, value, checked)
127
129 if self.digest_is_default:
130 checked = ' CHECKED'
131 else:
132 checked = ''
133 return '<input type=radio name="digest" value="1"%s>' % checked
134
135 def FormatDisabledNotice(self, user):
136 status = self.getDeliveryStatus(user)
137 reason = None
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')
150 if reason:
151 note = FontSize('+1', _(
152 'Note: your list delivery is currently disabled%(reason)s.'
153 )).Format()
154 link = Link('#disable', _('Mail delivery')).Format()
155 mailto = Link('mailto:' + self.GetOwnerEmail(),
156 _('the list administrator')).Format()
157 return _('''<p>%(note)s
158
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.
167 score = info.score
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.''')
175 else:
176 return ''
177
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>")
184 else:
185 return ""
186
188 msg = ''
189 also = ''
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.""")
197 also = _('also ')
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.""")
204 also = _("also ")
205 if msg:
206 msg += ' '
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.''')
213 else:
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).''')
219
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.)""")
226 return msg
227
229 if self.digest_is_default:
230 checked = ''
231 else:
232 checked = ' CHECKED'
233 return '<input type=radio name="digest" value="0"%s>' % checked
234
236 if self.mime_is_default_digest:
237 checked = ' CHECKED'
238 else:
239 checked = ''
240 return '<input type=radio name="mime" value="1"%s>' % checked
241
243 if self.mime_is_default_digest:
244 checked = ''
245 else:
246 checked = ' CHECKED'
247 return '<input type=radio name="plain" value="1"%s>' % checked
248
249 def FormatEditingOption(self, lang):
250 if self.private_roster == 0:
251 either = _('<b><i>either</i></b> ')
252 else:
253 either = ''
254 realname = self.real_name
255
256 text = (_('''To unsubscribe from %(realname)s, get a password reminder,
257 or change your subscription options %(either)senter your subscription
258 email address:
259 <p><center> ''')
260 + TextBox('email', size=30).Format()
261 + ' '
262 + SubmitButton('UserOptions',
263 _('Unsubscribe or edit options')).Format()
264 + Hidden('language', lang).Format()
265 + '</center>')
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''')
271 return text
272
273 def RestrictedListMessage(self, which, restriction):
274 if not restriction:
275 return ''
276 elif restriction == 1:
277 return _(
278 '''(<i>%(which)s is only available to the list
279 members.</i>)''')
280 else:
281 return _('''(<i>%(which)s is only available to the list
282 administrator.</i>)''')
283
285 return self.RosterOption(lang).Format()
286
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 ")
292 + self.real_name
293 + _(" subscribers: "))
294 container.AddItem(SubmitButton('SubscriberRoster',
295 _("Visit Subscriber list")))
296 else:
297 if self.private_roster == 1:
298 only = _('members')
299 whom = _('Address:')
300 else:
301 only = _('the list administrator')
302 whom = _('Admin address:')
303 # Solicit the user and password.
304 container.AddItem(
305 self.RestrictedListMessage(_('The subscribers list'),
306 self.private_roster)
307 + _(" <p>Enter your ")
308 + whom[:-1].lower()
309 + _(" and password to visit"
310 " the subscribers list: <p><center> ")
311 + whom
312 + " ")
313 container.AddItem(self.FormatBox('roster-email'))
314 container.AddItem(_("Password: ")
315 + self.FormatSecureBox('roster-pw')
316 + "&nbsp;&nbsp;")
317 container.AddItem(SubmitButton('SubscriberRoster',
318 _('Visit Subscriber List')))
319 container.AddItem("</center>")
320 return container
321
322 def FormatFormStart(self, name, extra='',
323 mlist=None, contexts=None, user=None):
324 base_url = self.GetScriptURL(name)
325 if extra:
326 full_url = "%s/%s" % (base_url, extra)
327 else:
328 full_url = base_url
329 if mlist:
330 return ("""<form method="POST" action="%s">
331<input type="hidden" name="csrf_token" value="%s">"""
332 % (full_url, csrf_token(mlist, contexts, user)))
333 return ('<FORM Method=POST ACTION="%s">' % full_url)
334
336 return '<a href="%s">' % self.GetBaseArchiveURL()
337
338 def FormatFormEnd(self):
339 return '</FORM>'
340
341 def FormatBox(self, name, size=20, value=''):
342 if isinstance(value, str):
343 safevalue = Utils.websafe(value)
344 else:
345 safevalue = value
346 return '<INPUT type="Text" name="%s" size="%d" value="%s">' % (
347 name, size, safevalue)
348
349 def FormatSecureBox(self, name):
350 return '<INPUT type="Password" name="%s" size="15">' % name
351
352 def FormatButton(self, name, text='Submit'):
353 return '<INPUT type="Submit" name="%s" value="%s">' % (name, text)
354
355 def FormatReminder(self, lang):
356 if self.send_reminders:
357 return _('Once a month, your password will be emailed to you as'
358 ' a reminder.')
359 return ''
360
361 def ParseTags(self, template, replacements, lang=None):
362 if lang is None:
363 charset = 'us-ascii'
364 else:
365 charset = Utils.GetCharSet(lang)
366 text = Utils.maketext(template, raw=1, lang=lang, mlist=self)
367 parts = re.split('(</?[Mm][Mm]-[^>]*>)', text)
368 i = 1
369 while i < len(parts):
370 tag = parts[i].lower()
371 if replacements.has_key(tag):
372 repl = replacements[tag]
373 if isinstance(repl, type(u'')):
374 repl = repl.encode(charset, 'replace')
375 parts[i] = repl
376 else:
377 parts[i] = ''
378 i = i + 2
379 return EMPTYSTRING.join(parts)
380
381 # This needs to wait until after the list is inited, so let's build it
382 # when it's needed only.
383 def GetStandardReplacements(self, lang=None):
384 dmember_len = len(self.getDigestMemberKeys())
385 member_len = len(self.getRegularMemberKeys())
386 # If only one language is enabled for this mailing list, omit the
387 # language choice buttons.
388 if len(self.GetAvailableLanguages()) == 1:
389 listlangs = _(Utils.GetLanguageDescr(self.preferred_language))
390 else:
391 listlangs = self.GetLangSelectBox(lang).Format()
392 if lang:
393 cset = Utils.GetCharSet(lang) or 'us-ascii'
394 else:
395 cset = Utils.GetCharSet(self.preferred_language) or 'us-ascii'
396 d = {
397 '<mm-mailman-footer>' : self.GetMailmanFooter(),
398 '<mm-list-name>' : self.real_name,
399 '<mm-email-user>' : self._internal_name,
400 '<mm-list-description>' :
401 Utils.websafe(self.GetDescription(cset)),
402 '<mm-list-info>' :
403 '<!---->' + BR.join(self.info.split(NL)) + '<!---->',
404 '<mm-form-end>' : self.FormatFormEnd(),
405 '<mm-archive>' : self.FormatArchiveAnchor(),
406 '</mm-archive>' : '</a>',
407 '<mm-list-subscription-msg>' : self.FormatSubscriptionMsg(),
408 '<mm-restricted-list-message>' : \
409 self.RestrictedListMessage(_('The current archive'),
410 self.archive_private),
411 '<mm-num-reg-users>' : `member_len`,
412 '<mm-num-digesters>' : `dmember_len`,
413 '<mm-num-members>' : (`member_len + dmember_len`),
414 '<mm-posting-addr>' : '%s' % self.GetListEmail(),
415 '<mm-request-addr>' : '%s' % self.GetRequestEmail(),
416 '<mm-owner>' : self.GetOwnerEmail(),
417 '<mm-reminder>' : self.FormatReminder(self.preferred_language),
418 '<mm-host>' : self.host_name,
419 '<mm-list-langs>' : listlangs,
420 }
421 if mm_cfg.IMAGE_LOGOS:
422 d['<mm-favicon>'] = mm_cfg.IMAGE_LOGOS + mm_cfg.SHORTCUT_ICON
423 return d
424
425 def GetAllReplacements(self, lang=None, list_hidden=False):
426 """
427 returns standard replaces plus formatted user lists in
428 a dict just like GetStandardReplacements.
429 """
430 if lang is None:
431 lang = self.preferred_language
432 d = self.GetStandardReplacements(lang)
433 d.update({"<mm-regular-users>": self.FormatUsers(0, lang, list_hidden),
434 "<mm-digest-users>": self.FormatUsers(1, lang, list_hidden)})
435 return d
436
437 def GetLangSelectBox(self, lang=None, varname='language'):
438 if lang is None:
439 lang = self.preferred_language
440 # Figure out the available languages
441 values = self.GetAvailableLanguages()
442 legend = map(_, map(Utils.GetLanguageDescr, values))
443 try:
444 selected = values.index(lang)
445 except ValueError:
446 try:
447 selected = values.index(self.preferred_language)
448 except ValueError:
449 selected = mm_cfg.DEFAULT_SERVER_LANGUAGE
450 # Return the widget
451 return SelectOptions(varname, values, legend, selected)
def FormatFormStart(self, name, extra='', mlist=None, contexts=None, user=None)
def FormatOptionButton(self, option, value, user)
def FormatUmbrellaNotice(self, user, type)
def GetLangSelectBox(self, lang=None, varname='language')
def FormatButton(self, name, text='Submit')
def FormatUsers(self, digest, lang=None, list_hidden=False)
def ParseTags(self, template, replacements, lang=None)
def FormatBox(self, name, size=20, value='')
def GetAllReplacements(self, lang=None, list_hidden=False)
def GetStandardReplacements(self, lang=None)
def RestrictedListMessage(self, which, restriction)
def csrf_token(mlist, contexts, user=None)
Definition: CSRFcheck.py:39