diff --git a/Mailman/Archiver/Archiver.py b/Mailman/Archiver/Archiver.py index 3289203..cae605b 100644 --- a/Mailman/Archiver/Archiver.py +++ b/Mailman/Archiver/Archiver.py @@ -72,6 +72,8 @@ class Archiver: self.archive_private = mm_cfg.DEFAULT_ARCHIVE_PRIVATE self.archive_volume_frequency = \ mm_cfg.DEFAULT_ARCHIVE_VOLUME_FREQUENCY + self.archive_idx_date_format = \ + mm_cfg.DEFAULT_ARCHIVE_IDX_DATE_FORMAT # The archive file structure by default is: # # archives/ diff --git a/Mailman/Archiver/HyperArch.py b/Mailman/Archiver/HyperArch.py index a532c81..9127def 100644 --- a/Mailman/Archiver/HyperArch.py +++ b/Mailman/Archiver/HyperArch.py @@ -32,6 +32,7 @@ import sys import re import errno import urllib +import datetime import time import os import types @@ -136,7 +137,7 @@ def sizeof(filename, lang): return ' %d MB ' % (size / 1000000) -html_charset = '' def CGIescape(arg, lang=None): @@ -491,20 +492,37 @@ class Article(pipermail.Article): charset = Utils.GetCharSet(self._lang) d["encoding"] = html_charset % charset + d['listid'] = self._mlist.internal_name() self._add_decoded(d) - return quick_maketext( + articletext = quick_maketext( 'article.html', d, lang=self._lang, mlist=self._mlist) + articletitle = quick_maketext('articletitle.html', d, + lang=self._lang, mlist=self._mlist) + + articleheader = quick_maketext('articleheader.html', d, + lang=self._lang, mlist=self._mlist) + + return quick_maketext( + 'tocpage.html', + {'innertext': articletext, + 'title': articletitle, + 'header': articleheader, + 'listid': self._mlist.internal_name(), + 'mailmanurl': '../../', + }, + lang=self._lang, mlist=self._mlist) + def _get_prev(self): """Return the href and subject for the previous message""" if self.prev: subject = self._get_subject_enc(self.prev) - prev = ('' + prev = ('' % (url_quote(self.prev.filename))) - prev_wsubj = ('
There currently are no publicly-advertised %(mailmanlink)s - mailing lists on %(hostname)s.'''), + mailing lists on %(hostname)s.
'''), ]) else: welcome.extend([ greeting, _('''Below is the collection of publicly-advertised %(mailmanlink)s mailing lists on %(hostname)s. Click on a list - name to visit the configuration pages for that list.'''), + name to visit the configuration pages for that list.
'''), ]) creatorurl = Utils.ScriptURL('create') @@ -279,35 +281,41 @@ def admin_overview(msg=''): '.)',
])
- table.AddRow([Container(*welcome)])
- table.AddCellInfo(max(table.GetCurrentRowIndex(), 0), 0, colspan=2)
+ d[' " % (varname, category, description))
+ doc.AddItem(Header(1, legend).Format())
+
+ d['title'] = _("Mailman %(varname)s List Option Help")
+
+ doc.AddItem(" " % elaboration)
+ doc.AddItem(" ')
- form.AddItem(Center(submit_button()))
- doc.AddItem(Center(form))
+ form.AddItem(submit_button().Format())
+ doc.AddItem(form)
- doc.AddItem(_("""Warning: changing this option here
+ doc.AddItem(_(""" ')
+ d['formtip'] = \
+ _(''' Make your changes in the following section, then submit them
+ using the Submit Your Changes button below. %s To view more members, click on the appropriate
- range listed below:''')
+ range listed below: ')
+ container.AddItem(footer + buttons.Format())
return container
@@ -1212,55 +1226,49 @@ def mass_remove(mlist, container):
def password_inputs(mlist):
adminurl = mlist.GetScriptURL('admin', absolute=1)
- table = Table(cellspacing=3, cellpadding=4)
- table.AddRow([Center(Header(2, _('Change list ownership passwords')))])
- table.AddCellInfo(table.GetCurrentRowIndex(), 0, colspan=2,
- bgcolor=mm_cfg.WEB_HEADER_COLOR)
- table.AddRow([_("""\
+ cnt = Container()
+ cnt.AddItem(_("""\
+
The list administrators are the people who have ultimate control over
all parameters of this mailing list. They are able to change any list
configuration variable available through these administration web pages.
+ The list moderators have more limited permissions; they are not
+
+The list moderators have more limited permissions; they are not
able to change any list configuration variable, but they are allowed to tend
to pending administration requests, including approving or rejecting held
subscription requests, and disposing of held postings. Of course, the
list administrators can also tend to pending requests.
+ In order to split the list ownership duties into administrators and
+
+In order to split the list ownership duties into administrators and
moderators, you must set a separate moderator password in the fields below,
and also provide the email addresses of the list moderators in the
-general options section.""")])
- table.AddCellInfo(table.GetCurrentRowIndex(), 0, colspan=2)
+general options section.
+ ')
+ d['subscribe_results'] += _('Successfully subscribed:')
+ d['subscribe_results'] += UnorderedList(*subscribe_success).Format()
if subscribe_errors:
if subscribe_or_invite:
- doc.AddItem(Header(5, _('Error inviting:')))
+ d['subscribe_results'] += _('Error inviting:')
else:
- doc.AddItem(Header(5, _('Error subscribing:')))
+ d['subscribe_results'] += _('Error subscribing:')
items = ['%s -- %s' % (x0, x1) for x0, x1 in subscribe_errors]
- doc.AddItem(UnorderedList(*items))
- doc.AddItem(' ')
+ d['subscribe_results'] += UnorderedList(*items).Format()
# Unsubscriptions
removals = ''
if cgidata.has_key('unsubscribees'):
@@ -1393,16 +1400,13 @@ def change_options(mlist, category, subcat, cgidata, doc):
unsubscribe_success.append(Utils.websafe(addr))
except Errors.NotAMemberError:
unsubscribe_errors.append(Utils.websafe(addr))
+ d['unsubscribe_results'] = ''
if unsubscribe_success:
- doc.AddItem(Header(5, _('Successfully Unsubscribed:')))
- doc.AddItem(UnorderedList(*unsubscribe_success))
- doc.AddItem(' ')
+ d['unsubscribe_results'] += _('Successfully Unsubscribed:')
+ d['unsubscribe_results'] += UnorderedList(*unsubscribe_success).Format()
if unsubscribe_errors:
- doc.AddItem(Header(3, Bold(FontAttr(
- _('Cannot unsubscribe non-members:'),
- color='#ff0000', size='+2')).Format()))
- doc.AddItem(UnorderedList(*unsubscribe_errors))
- doc.AddItem(' ')
+ d['unsubscribe_results'] += _('Cannot unsubscribe non-members:')
+ d['unsubscribe_results'] += UnorderedList(*unsubscribe_errors).Format()
# See if this was a moderation bit operation
if cgidata.has_key('allmodbit_btn'):
val = cgidata.getvalue('allmodbit_val')
@@ -1475,12 +1479,19 @@ def change_options(mlist, category, subcat, cgidata, doc):
else:
mlist.setMemberOption(user, opt_code, 0)
# Give some feedback on who's been removed
+ d['remove_results'] = ''
if removes:
- doc.AddItem(Header(5, _('Successfully Removed:')))
- doc.AddItem(UnorderedList(*removes))
- doc.AddItem(' ')
+ d['remove_results'] += _('Successfully Removed:')
+ d['remove_results'] += UnorderedList(*removes).Format()
if errors:
- doc.AddItem(Header(5, _("Error Unsubscribing:")))
+ d['remove_results'] += _("Error Unsubscribing:")
items = ['%s -- %s' % (x[0], x[1]) for x in errors]
- doc.AddItem(apply(UnorderedList, tuple((items))))
- doc.AddItem(" ")
+ d['remove_results'] += apply(UnorderedList, tuple((items))).Format()
+
+ for i in ['subscribe_results', 'unsubscribe_results', 'remove_results']:
+ if d.has_key(i):
+ d[i] = ' Invalid confirmation string:
+ %(safecookie)s. Note that confirmation strings expire approximately
%(days)s days after the initial subscription request. If your
confirmation has expired, please try to re-submit your subscription.
Otherwise, re-enter your confirmation
- string.''')
+ string. Or hit Cancel my subscription request if you no longer want to
- subscribe to this list.""") + ' Your confirmation is required in order to continue with
the subscription request to the mailing list %(listname)s.
Your subscription settings are shown below; make any necessary changes
and hit Subscribe to list ... to complete the confirmation
process. Once you've confirmed your subscription request, the
moderator must approve or reject your membership request. You will
- receive notice of their decision.
+ receive notice of their decision. Note: your password will be emailed to you once your subscription
is confirmed. You can change it by visiting your personal options
- page.
+ page. Or, if you've changed your mind and do not want to subscribe to
this mailing list, you can hit Cancel my subscription
- request.""") + ' You can now
proceed to your membership login
- page.'''))
+ page.''')
mlist.Save()
finally:
mlist.Unlock()
+ if innertext:
+ return innertext
+ else:
+ innertext = Utils.maketext("confirmation.html", d)
+ return innertext
+
-def unsubscription_cancel(mlist, doc, cookie):
+def unsubscription_cancel(mlist, d, cookie):
# Expunge this record from the pending database
expunge(mlist, cookie)
- doc.AddItem(_('You have canceled your unsubscription request.'))
+ d['title'] = _('Unsubscription canceled')
+ d['heading'] = d['title']
+ d['msg'] = _('You have canceled your unsubscription request.')
+ innertext = Utils.maketext("simplemsg.html", d)
+ return innertext
-def unsubscription_confirm(mlist, doc, cookie):
+def unsubscription_confirm(mlist, d, cookie):
+ d['error'] = ''
# See the comment in admin.py about the need for the signal
# handler.
def sigterm_handler(signum, frame, mlist=mlist):
@@ -428,41 +473,41 @@ def unsubscription_confirm(mlist, doc, cookie):
op, addr = mlist.pend_confirm(cookie, expunge=False)
lang = mlist.getMemberLanguage(addr)
i18n.set_language(lang)
- doc.set_language(lang)
op, addr = mlist.ProcessConfirmation(cookie)
except Errors.NotAMemberError:
- bad_confirmation(doc, _('''Invalid confirmation string. It is
+ bad_confirmation(d, _('''Invalid confirmation string. It is
possible that you are attempting to confirm a request for an
address that has already been unsubscribed.'''))
else:
# The response
listname = mlist.real_name
title = _('Unsubscription request confirmed')
+ d['title'] = title
+ d['heading'] = title
listinfourl = mlist.GetScriptURL('listinfo', absolute=1)
- doc.SetTitle(title)
- doc.AddItem(Header(3, Bold(FontAttr(title, size='+2'))))
- doc.AddItem(_("""\
+ d['confirm_msg'] = _("""\
You have successfully unsubscribed from the %(listname)s mailing
list. You can now visit the list's main
- information page."""))
+ information page.""")
mlist.Save()
finally:
mlist.Unlock()
+ innertext = Utils.maketext("confirmation.html", d)
+ return innertext
+
-def unsubscription_prompt(mlist, doc, cookie, addr):
+def unsubscription_prompt(mlist, d, cookie, addr):
title = _('Confirm unsubscription request')
- doc.SetTitle(title)
+ d['title'] = title
+ d['heading'] = title
+ d['extra'] = ''
+
lang = mlist.getMemberLanguage(addr)
i18n.set_language(lang)
- doc.set_language(lang)
- form = Form(mlist.GetScriptURL('confirm', 1))
- table = Table(border=0, width='100%')
- table.AddRow([Center(Bold(FontAttr(title, size='+1')))])
- table.AddCellInfo(table.GetCurrentRowIndex(), 0,
- colspan=2, bgcolor=mm_cfg.WEB_HEADER_COLOR)
+ d['formaction'] = mlist.GetScriptURL('confirm', 1)
listname = mlist.real_name
fullname = mlist.getMemberName(addr)
@@ -470,7 +515,8 @@ def unsubscription_prompt(mlist, doc, cookie, addr):
fullname = _('Not available')
else:
fullname = Utils.uncanonstr(fullname, lang)
- table.AddRow([_("""Your confirmation is required in order to complete the
+
+ d['instructions'] = _("""Your confirmation is required in order to complete the
unsubscription request from the mailing list %(listname)s. You
are currently subscribed with
@@ -482,15 +528,13 @@ def unsubscription_prompt(mlist, doc, cookie, addr):
process.
Or hit Cancel and discard to cancel this unsubscription
- request.""") + ' You can create a new mailing list by entering
+ the relevant information into the form below. The name of the mailing list
will be used as the primary address for posting messages to the list, so
it should be lowercased. You will not be able to change this once the
- list is created.
+ list is created. You also need to enter the email address of the initial list owner.
Once the list is created, the list owner will be given notification, along
with the initial list password. The list owner will then be able to
- modify the password and add or remove additional list owners.
+ modify the password and add or remove additional list owners. If you want Mailman to automatically generate the initial list admin
password, click on `Yes' in the autogenerate field below, and leave the
- initial list password fields empty.
+ initial list password fields empty. You must have the proper authorization to create new mailing lists.
Each site should have a list creator's password, which you can
enter in the field at the bottom. Note that the site administrator's
- password can also be used for authentication.
- """)])
+ password can also be used for authentication. There currently are no publicly-advertised
- %(mailmanlink)s mailing lists on %(hostname)s.'''))
+ %(mailmanlink)s mailing lists on %(hostname)s. Below is a listing of all the public mailing lists on
%(hostname)s. Click on a list name to get more information about
the list, or to subscribe, unsubscribe, and change the preferences
- on your subscription.'''))
+ on your subscription. To visit the general information page for an unadvertised
+ list, open a URL similar to this one, but with a '/' and the %(adj)s
+ list name appended. List administrators, you can visit '''),
Link(Utils.ScriptURL('admin'),
- _('the list admin overview page')),
- _(''' to find the management interface for your list.
+ _('the list admin overview page')).Format(),
+ _(''' or '''),
+ Link(Utils.ScriptURL('manager'),
+ _('the list manager overview page')).Format(),
+ _(''' to find the management interface for your list. If you are having trouble using the lists, please contact '''),
- Link('mailto:' + siteowner, siteowner),
- '. '))
+ Link('mailto:' + siteowner, siteowner).Format(),
+ '.
')
- doc.AddItem(MailmanLogo())
- print doc.Format()
+ d['
%(varname)s Option""")
- header = Table(width='100%')
- header.AddRow([Center(Header(3, legend))])
- header.AddCellInfo(header.GetCurrentRowIndex(), 0, colspan=2,
- bgcolor=mm_cfg.WEB_HEADER_COLOR)
- doc.SetTitle(_("Mailman %(varname)s List Option Help"))
- doc.AddItem(header)
- doc.AddItem("%s (%s): %s
%(label)s Section'))))
- doc.AddItem('
')
+ d['title'] = _('%(realname)s Administration (%(label)s)')
+ d['heading'] = _('%(realname)s mailing list administration
%(label)s Section')
+
# Now we need to craft the form that will be submitted, which will contain
# all the variable settings, etc. This is a bit of a kludge because we
# know that the autoreply and members categories supports file uploads.
- encoding = None
+ encoding = ''
if category in ('autoreply', 'members'):
encoding = 'multipart/form-data'
+ d['form_encoding'] = encoding
if subcat:
- form = Form('%s/%s/%s' % (adminurl, category, subcat),
- encoding=encoding)
+ d['form_action'] = '%s/%s/%s' % (adminurl, category, subcat)
else:
- form = Form('%s/%s' % (adminurl, category), encoding=encoding)
+ d['form_action'] = '%s/%s' % (adminurl, category)
+
# This holds the two columns of links
- linktable = Table(valign='top', width='100%')
- linktable.AddRow([Center(Bold(_("Configuration Categories"))),
- Center(Bold(_("Other Administrative Activities")))])
+ d['configuration_categories_heading'] = _("Configuration Categories")
+ d['admin_activities_heading'] = _("Other Administrative Activities")
+
# The `other links' are stuff in the right column.
- otherlinks = UnorderedList()
+ otherlinks = UnorderedList(id='otherlinks')
otherlinks.AddItem(Link(mlist.GetScriptURL('admindb'),
- _('Tend to pending moderator requests')))
+ _('Tend to pending moderator requests')).Format())
otherlinks.AddItem(Link(mlist.GetScriptURL('listinfo'),
- _('Go to the general list information page')))
+ _('Go to the general list information page')).Format())
otherlinks.AddItem(Link(mlist.GetScriptURL('edithtml'),
- _('Edit the public HTML pages and text files')))
+ _('Edit the public HTML pages and text files')).Format())
otherlinks.AddItem(Link(mlist.GetBaseArchiveURL(),
- _('Go to list archives')).Format() +
- '
')
+ _('Go to list archives'), id='archiveslink').Format())
+
# We do not allow through-the-web deletion of the site list!
if mm_cfg.OWNERS_CAN_DELETE_THEIR_OWN_LISTS and \
mlist.internal_name() <> mm_cfg.MAILMAN_SITE_LIST:
otherlinks.AddItem(Link(mlist.GetScriptURL('rmlist'),
_('Delete this mailing list')).Format() +
- _(' (requires confirmation)
'))
+ _(' (requires confirmation)'),
+ id="rmlistlink")
otherlinks.AddItem(Link('%s/logout' % adminurl,
- # BAW: What I really want is a blank line, but
- # adding an won't do it because of the
- # bullet added to the list item.
- '%s' %
- _('Logout')))
+ '%s' % _('Logout')),
+ id="logoutlink")
+
# These are links to other categories and live in the left column
- categorylinks_1 = categorylinks = UnorderedList()
+ categorylinks = UnorderedList(id='categorylinks_1', classname='categorylinks')
categorylinks_2 = ''
categorykeys = categories.keys()
half = len(categorykeys) / 2
@@ -449,107 +468,112 @@ def show_results(mlist, doc, category, subcat, cgidata):
else:
# The first subcategory in the list is the default
subcat = subcats[0][0]
- subcat_items = []
+ subcat_items = UnorderedList(classname="subcategory")
for sub, text in subcats:
+ classname = None
if sub == subcat:
- text = Bold('[%s]' % text).Format()
- subcat_items.append(Link(url + '/' + sub, text))
+ text = '[%s]' % text
+ classname = "selected"
+ subcat_items.AddItem(Link(url + '/' + sub, text), classname=classname)
categorylinks.AddItem(
- Bold(label).Format() +
- UnorderedList(*subcat_items).Format())
+ label +
+ subcat_items.Format(),
+ classname='expanded')
else:
- categorylinks.AddItem(Link(url, Bold('[%s]' % label)))
+ categorylinks.AddItem(Link(url, '[%s]' % label), classname='selected')
else:
categorylinks.AddItem(Link(url, label))
counter += 1
if counter >= half:
- categorylinks_2 = categorylinks = UnorderedList()
+ categorylinks_1 = categorylinks
+ categorylinks_2 = categorylinks = UnorderedList(id='categorylinks_2', classname='categorylinks')
counter = -len(categorykeys)
+
+ d['categorylinks_1'] = categorylinks_1.Format()
+ d['categorylinks_2'] = categorylinks_2.Format()
+
# Make the emergency stop switch a rude solo light
- etable = Table()
- # Add all the links to the links table...
- etable.AddRow([categorylinks_1, categorylinks_2])
- etable.AddRowInfo(etable.GetCurrentRowIndex(), valign='top')
+ d['e_mod'] = ''
if mlist.emergency:
label = _('Emergency moderation of all list traffic is enabled')
- etable.AddRow([Center(
- Link('?VARHELP=general/emergency', Bold(label)))])
- color = mm_cfg.WEB_ERROR_COLOR
- etable.AddCellInfo(etable.GetCurrentRowIndex(), 0,
- colspan=2, bgcolor=color)
- linktable.AddRow([etable, otherlinks])
+ d['e_mod'] = '
')
- form.AddItem(
- _('''Make your changes in the following section, then submit them
- using the Submit Your Changes button below.''')
- + '
\n')
+ contents.append(Header(2, _('Additional Member Tasks')).Format())
table = Table(width='100%')
- table.AddRow([Center(Header(2, _('Additional Member Tasks')))])
- table.AddCellInfo(table.GetCurrentRowIndex(), 0, colspan=2,
- bgcolor=mm_cfg.WEB_HEADER_COLOR)
- # Add a blank separator row
- table.AddRow([' ', ' '])
# Add a section to set the moderation bit for all members
- table.AddRow([_("""
""")])
table.AddCellInfo(table.GetCurrentRowIndex(), 0, colspan=2)
table.AddRow([RadioButtonArray('allmodbit_val',
(_('Off'), _('On')),
mlist.default_member_moderation),
SubmitButton('allmodbit_btn', _('Set'))])
- form.AddItem(table)
+ contents.append(table.Format())
+ d['form'] = ' '.join(contents)
elif category == 'passwords':
- form.AddItem(Center(password_inputs(mlist)))
- form.AddItem(Center(submit_button()))
+ d['label'] = _('Change list ownership passwords')
+ d['form'] = password_inputs(mlist).Format()
+ d['submit_button'] = submit_button(id='formsubmit').Format()
else:
- form.AddItem(show_variables(mlist, category, subcat, cgidata, doc))
- form.AddItem(Center(submit_button()))
+ d['form'] = show_variables(mlist, category, subcat, cgidata, doc)
+ d['submit_button'] = submit_button(id='formsubmit').Format()
# And add the form
- doc.AddItem(form)
- doc.AddItem(mlist.GetMailmanFooter())
+ d['mailmanfooter'] = mlist.GetMailmanFooter()
+ # doc.AddItem(str(d))
+ d['innertext'] = Utils.maketext("listadmin.html", d, mlist=mlist)
+ d['mailmanurl'] = Utils.ScriptURL('')
+ page = Utils.maketext("page.html", d, mlist=mlist)
+ doc.AddItem(page)
+ # doc.AddItem(content)
+ doc.Format()
def show_variables(mlist, category, subcat, cgidata, doc):
options = mlist.GetConfigInfo(category, subcat)
+ container = Container()
# The table containing the results
table = Table(cellspacing=3, cellpadding=4, width='100%')
# Get and portray the text label for the category.
categories = mlist.GetConfigCategories()
- label = _(categories[category][0])
-
- table.AddRow([Center(Header(2, label))])
- table.AddCellInfo(table.GetCurrentRowIndex(), 0, colspan=2,
- bgcolor=mm_cfg.WEB_HEADER_COLOR)
# The very first item in the config info will be treated as a general
# description if it is a string
description = options[0]
if isinstance(description, StringType):
- table.AddRow([description])
- table.AddCellInfo(table.GetCurrentRowIndex(), 0, colspan=2)
+ container.AddItem('
'])
table.AddCellInfo(table.GetCurrentRowIndex(), 0, colspan=2)
- return table
+ return container.Format()
@@ -825,31 +849,21 @@ def get_item_gui_description(mlist, category, subcat,
-def membership_options(mlist, subcat, cgidata, doc, form):
+def membership_options(mlist, subcat, cgidata, doc, d):
# Show the main stuff
adminurl = mlist.GetScriptURL('admin', absolute=1)
container = Container()
- header = Table(width="100%")
# If we're in the list subcategory, show the membership list
if subcat == 'add':
- header.AddRow([Center(Header(2, _('Mass Subscriptions')))])
- header.AddCellInfo(header.GetCurrentRowIndex(), 0, colspan=2,
- bgcolor=mm_cfg.WEB_HEADER_COLOR)
- container.AddItem(header)
+ container.AddItem(Header(2, _('Mass Subscriptions')))
mass_subscribe(mlist, container)
return container
if subcat == 'remove':
- header.AddRow([Center(Header(2, _('Mass Removals')))])
- header.AddCellInfo(header.GetCurrentRowIndex(), 0, colspan=2,
- bgcolor=mm_cfg.WEB_HEADER_COLOR)
- container.AddItem(header)
+ container.AddItem(Header(2, _('Mass Removals')))
mass_remove(mlist, container)
return container
# Otherwise...
- header.AddRow([Center(Header(2, _('Membership List')))])
- header.AddCellInfo(header.GetCurrentRowIndex(), 0, colspan=2,
- bgcolor=mm_cfg.WEB_HEADER_COLOR)
- container.AddItem(header)
+ container.AddItem(Header(2, _('Membership List')))
# Add a "search for member" button
table = Table(width='100%')
link = Link('http://docs.python.org/library/re.html'
@@ -909,7 +923,7 @@ def membership_options(mlist, subcat, cgidata, doc, form):
members = buckets[bucket]
action = adminurl + '/members?letter=%s' % bucket
if len(members) <= chunksz:
- form.set_action(action)
+ d['form_action'] = action
else:
i, r = divmod(len(members), chunksz)
numchunks = i + (not not r * 1)
@@ -924,7 +938,7 @@ def membership_options(mlist, subcat, cgidata, doc, form):
chunkindex = 0
members = members[chunkindex*chunksz:(chunkindex+1)*chunksz]
# And set the action URL
- form.set_action(action + '&chunk=%s' % chunkindex)
+ d['form_action'] = action + '&chunk=%s' % chunkindex
# So now members holds all the addresses we're going to display
allcnt = len(all)
if bucket:
@@ -1112,7 +1126,7 @@ def membership_options(mlist, subcat, cgidata, doc, form):
buttons = []
url = adminurl + '/members?%sletter=%s&' % (addlegend, bucket)
footer = _('''
'
+ subscribe to this list.""")
if mlist.subscribe_policy in (2, 3):
# Confirmation is required
- result = _("""Your confirmation is required in order to continue with
+ result = _("""
'
- table.AddRow([result])
- table.AddCellInfo(table.GetCurrentRowIndex(), 0, colspan=2)
+ request.""")
+ d['confirm_msg'] = result
table.AddRow([Label(_('Your email address:')), email])
table.AddRow([Label(_('Your real name:')),
@@ -304,11 +332,12 @@ def subscription_prompt(mlist, doc, cookie, userdesc):
SubmitButton('submit', _('Subscribe to list %(listname)s'))
])
form.AddItem(table)
- doc.AddItem(form)
-
+ d['confirm_msg'] += form.Format()
+ innertext = Utils.maketext("confirmation.html", d)
+ return innertext
-def subscription_cancel(mlist, doc, cookie):
+def subscription_cancel(mlist, d, cookie):
mlist.Lock()
try:
# Discard this cookie
@@ -317,12 +346,17 @@ def subscription_cancel(mlist, doc, cookie):
mlist.Unlock()
lang = userdesc.language
i18n.set_language(lang)
- doc.set_language(lang)
- doc.AddItem(_('You have canceled your subscription request.'))
+ d['lang'] = lang
+ d['confirm_msg'] = _('You have canceled your subscription request.')
+ d['title'] = d['confirm_msg']
+ d['heading'] = d['confirm_msg']
+ d['errors'] = ''
+ innertext = Utils.maketext("confirmation.html", d)
+ return innertext
-def subscription_confirm(mlist, doc, cookie, cgidata):
+def subscription_confirm(mlist, d, cookie, cgidata):
# See the comment in admin.py about the need for the signal
# handler.
def sigterm_handler(signum, frame, mlist=mlist):
@@ -331,6 +365,8 @@ def subscription_confirm(mlist, doc, cookie, cgidata):
listname = mlist.real_name
mlist.Lock()
+ innertext = ''
+ d['errors'] = ''
try:
try:
# Some pending values may be overridden in the form. email of
@@ -339,7 +375,7 @@ def subscription_confirm(mlist, doc, cookie, cgidata):
if not Utils.IsLanguage(lang):
lang = mlist.preferred_language
i18n.set_language(lang)
- doc.set_language(lang)
+ d['lang'] = lang
if cgidata.has_key('digests'):
try:
digest = int(cgidata.getvalue('digests'))
@@ -356,42 +392,40 @@ def subscription_confirm(mlist, doc, cookie, cgidata):
op, addr, pw, digest, lang = mlist.ProcessConfirmation(
cookie, userdesc)
except Errors.MMNeedApproval:
- title = _('Awaiting moderator approval')
- doc.SetTitle(title)
- doc.AddItem(Header(3, Bold(FontAttr(title, size='+2'))))
- doc.AddItem(_("""\
+ d['title'] = _('Awaiting moderator approval')
+ d['heading'] = d['title']
+ d['confirm_msg'] = _("""\
You have successfully confirmed your subscription request to the
mailing list %(listname)s, however final approval is required from
the list moderator before you will be subscribed. Your request
has been forwarded to the list moderator, and you will be notified
- of the moderator's decision."""))
+ of the moderator's decision.""")
except Errors.NotAMemberError:
- bad_confirmation(doc, _('''Invalid confirmation string. It is
+ innertext = bad_confirmation(d, _('''Invalid confirmation string. It is
possible that you are attempting to confirm a request for an
address that has already been unsubscribed.'''))
except Errors.MMAlreadyAMember:
- doc.addError(_("You are already a member of this mailing list!"))
+ d['errors'] += _("You are already a member of this mailing list!")
except Errors.MembershipIsBanned:
owneraddr = mlist.GetOwnerEmail()
- doc.addError(_("""You are currently banned from subscribing to
+ d['errors'] += _("""You are currently banned from subscribing to
this list. If you think this restriction is erroneous, please
- contact the list owners at %(owneraddr)s."""))
+ contact the list owners at %(owneraddr)s.""")
except Errors.HostileSubscriptionError:
- doc.addError(_("""\
+ d['errors'] += _("""\
You were not invited to this mailing list. The invitation has
been discarded, and both list administrators have been
- alerted."""))
+ alerted.""")
else:
# Use the user's preferred language
i18n.set_language(lang)
- doc.set_language(lang)
+ d['lang'] = lang
# The response
listname = mlist.real_name
- title = _('Subscription request confirmed')
+ d['title'] = _('Subscription request confirmed')
optionsurl = mlist.GetOptionsURL(addr, absolute=1)
- doc.SetTitle(title)
- doc.AddItem(Header(3, Bold(FontAttr(title, size='+2'))))
- doc.AddItem(_('''\
+ d['heading'] = d['title']
+ d['confirm_msg'] = _('''\
You have successfully confirmed your subscription request for
"%(addr)s" to the %(listname)s mailing list. A separate
confirmation message will be sent to your email address, along
@@ -399,21 +433,32 @@ def subscription_confirm(mlist, doc, cookie, cgidata):
'])
- table.AddCellInfo(table.GetCurrentRowIndex(), 0, colspan=2)
- table.AddRow([Hidden('cookie', cookie)])
- table.AddCellInfo(table.GetCurrentRowIndex(), 0, colspan=2)
- table.AddRow([SubmitButton('submit', _('Unsubscribe')),
- SubmitButton('cancel', _('Cancel and discard'))])
+ request.""")
+ d['table'] = Hidden('cookie', cookie).Format()
+ d['table'] += SubmitButton('submit', _('Unsubscribe')).Format()
+ d['table'] += SubmitButton('cancel', _('Cancel and discard')).Format()
- form.AddItem(table)
- doc.AddItem(form)
+ innertext = Utils.maketext("askforcookie.html", d)
+ return innertext
diff --git a/Mailman/Cgi/create.py b/Mailman/Cgi/create.py
index 7e21b98..e40d51b 100644
--- a/Mailman/Cgi/create.py
+++ b/Mailman/Cgi/create.py
@@ -28,6 +28,7 @@ from Mailman import MailList
from Mailman import Message
from Mailman import Errors
from Mailman import i18n
+from Mailman import Utils
from Mailman.htmlformat import *
from Mailman.Logging.Syslog import syslog
from Mailman.Utils import sha_new
@@ -39,7 +40,8 @@ i18n.set_language(mm_cfg.DEFAULT_SERVER_LANGUAGE)
def main():
- doc = Document()
+ doc = HeadlessDocument()
+ d = {}
doc.set_language(mm_cfg.DEFAULT_SERVER_LANGUAGE)
cgidata = cgi.FieldStorage()
@@ -47,32 +49,38 @@ def main():
if parts:
# Bad URL specification
title = _('Bad URL specification')
- doc.SetTitle(title)
- doc.AddItem(
- Header(3, Bold(FontAttr(title, color='#ff0000', size='+2'))))
+ d['title'] = title
+ d['heading'] = title
syslog('error', 'Bad URL specification: %s', parts)
elif cgidata.has_key('doit'):
# We must be processing the list creation request
- process_request(doc, cgidata)
+ d['innertext'] = process_request(d, cgidata)
elif cgidata.has_key('clear'):
- request_creation(doc)
+ d['innertext'] = request_creation(d)
else:
# Put up the list creation request form
- request_creation(doc)
- doc.AddItem('
')
+ d['innertext'] = request_creation(d)
# Always add the footer and print the document
- doc.AddItem(_('Return to the ') +
+ div = Container()
+ div.AddItem(_('Return to the ') +
Link(Utils.ScriptURL('listinfo'),
_('general list overview')).Format())
- doc.AddItem(_('
Return to the ') +
+ div.AddItem(_('
Return to the ') +
Link(Utils.ScriptURL('admin'),
_('administrative list overview')).Format())
- doc.AddItem(MailmanLogo())
+ div.AddItem(MailmanLogo())
+
+ d['footer'] = div.Format()
+
+ d['mailmanurl'] = Utils.ScriptURL('')
+ page = Utils.maketext("page.html", d)
+ doc.AddItem(page)
print doc.Format()
-def process_request(doc, cgidata):
+def process_request(d, cgidata):
+ innertext = ''
# Lowercase the listname since this is treated as the "internal" name.
listname = cgidata.getvalue('listname', '').strip().lower()
owner = cgidata.getvalue('owner', '').strip()
@@ -100,49 +108,56 @@ def process_request(doc, cgidata):
# Sanity check
safelistname = Utils.websafe(listname)
if '@' in listname:
- request_creation(doc, cgidata,
+ innertext = request_creation(d, cgidata,
_('List name must not include "@": %(safelistname)s'))
- return
+ d['headingerror'] = 'error'
+ return innertext
if Utils.list_exists(listname):
# BAW: should we tell them the list already exists? This could be
# used to mine/guess the existance of non-advertised lists. Then
# again, that can be done in other ways already, so oh well.
- request_creation(doc, cgidata,
+ innertext = request_creation(d, cgidata,
_('List already exists: %(safelistname)s'))
- return
+ d['headingerror'] = 'error'
+ return innertext
if not listname:
- request_creation(doc, cgidata,
+ innertext = request_creation(d, cgidata,
_('You forgot to enter the list name'))
- return
+ d['headingerror'] = 'error'
+ return innertext
if not owner:
- request_creation(doc, cgidata,
+ innertext = request_creation(d, cgidata,
_('You forgot to specify the list owner'))
- return
+ d['headingerror'] = 'error'
+ return innertext
if autogen:
if password or confirm:
- request_creation(
- doc, cgidata,
+ innertext = request_creation(
+ d, cgidata,
_('''Leave the initial password (and confirmation) fields
blank if you want Mailman to autogenerate the list
passwords.'''))
- return
+ d['headingerror'] = 'error'
+ return innertext
password = confirm = Utils.MakeRandomPassword(
mm_cfg.ADMIN_PASSWORD_LENGTH)
else:
if password <> confirm:
- request_creation(doc, cgidata,
+ innertext = request_creation(d, cgidata,
_('Initial list passwords do not match'))
- return
+ d['headingerror'] = 'error'
+ return innertext
if not password:
- request_creation(
- doc, cgidata,
+ innertext = request_creation(
+ d, cgidata,
# The little tag is used so that this string
# differs from the one in bin/newlist. The former is destined
# for the web while the latter is destined for email, so they
# must be different entries in the message catalog.
_('The list password cannot be empty'))
- return
+ d['headingerror'] = 'error'
+ return innertext
# The authorization password must be non-empty, and it must match either
# the list creation password or the site admin password
ok = 0
@@ -151,18 +166,20 @@ def process_request(doc, cgidata):
if not ok:
ok = Utils.check_global_password(auth)
if not ok:
- request_creation(
- doc, cgidata,
+ innertext = request_creation(
+ d, cgidata,
_('You are not authorized to create new mailing lists'))
- return
+ d['headingerror'] = 'error'
+ return innertext
# Make sure the web hostname matches one of our virtual domains
hostname = Utils.get_domain()
if mm_cfg.VIRTUAL_HOST_OVERVIEW and \
not mm_cfg.VIRTUAL_HOSTS.has_key(hostname):
safehostname = Utils.websafe(hostname)
- request_creation(doc, cgidata,
+ innertext = request_creation(d, cgidata,
_('Unknown virtual host: %(safehostname)s'))
- return
+ d['headingerror'] = 'error'
+ return innertext
emailhost = mm_cfg.VIRTUAL_HOSTS.get(hostname, mm_cfg.DEFAULT_EMAIL_HOST)
# We've got all the data we need, so go ahead and try to create the list
# See admin.py for why we need to set up the signal handler.
@@ -195,28 +212,32 @@ def process_request(doc, cgidata):
s = Utils.websafe(e.args[0])
else:
s = Utils.websafe(owner)
- request_creation(doc, cgidata,
+ innertext = request_creation(d, cgidata,
_('Bad owner email address: %(s)s'))
- return
+ d['headingerror'] = 'error'
+ return innertext
except Errors.MMListAlreadyExistsError:
# MAS: List already exists so we don't need to websafe it.
- request_creation(doc, cgidata,
+ innertext = request_creation(d, cgidata,
_('List already exists: %(listname)s'))
- return
+ d['headingerror'] = 'error'
+ return innertext
except Errors.BadListNameError, e:
if e.args:
s = Utils.websafe(e.args[0])
else:
s = Utils.websafe(listname)
- request_creation(doc, cgidata,
+ innertext = request_creation(d, cgidata,
_('Illegal list name: %(s)s'))
- return
+ d['headingerror'] = 'error'
+ return innertext
except Errors.MMListError:
- request_creation(
- doc, cgidata,
+ innertext = request_creation(
+ d, cgidata,
_('''Some unknown error occurred while creating the list.
Please contact the site administrator for assistance.'''))
- return
+ d['headingerror'] = 'error'
+ return innertext
# Initialize the host_name and web_page_url attributes, based on
# virtual hosting settings and the request environment variables.
@@ -261,7 +282,8 @@ def process_request(doc, cgidata):
create_url = Utils.ScriptURL('create')
title = _('Mailing list creation results')
- doc.SetTitle(title)
+ d['title'] = title
+
table = Table(border=0, width='100%')
table.AddRow([Center(Bold(FontAttr(title, size='+1')))])
table.AddCellInfo(table.GetCurrentRowIndex(), 0,
@@ -274,7 +296,8 @@ def process_request(doc, cgidata):
ullist.AddItem(Link(admin_url, _("Visit the list's admin page")))
ullist.AddItem(Link(create_url, _('Create another list')))
table.AddRow([ullist])
- doc.AddItem(table)
+ innertext = table.Format()
+ return innertext
@@ -286,44 +309,39 @@ dummy = Dummy()
-def request_creation(doc, cgidata=dummy, errmsg=None):
+def request_creation(d, cgidata=dummy, errmsg=None):
# What virtual domain are we using?
hostname = Utils.get_domain()
# Set up the document
- title = _('Create a %(hostname)s Mailing List')
- doc.SetTitle(title)
- table = Table(border=0, width='100%')
- table.AddRow([Center(Bold(FontAttr(title, size='+1')))])
- table.AddCellInfo(table.GetCurrentRowIndex(), 0,
- bgcolor=mm_cfg.WEB_HEADER_COLOR)
+ d['title'] = _('Create a %(hostname)s Mailing List')
+ d['heading'] = d['title']
+
# Add any error message
if errmsg:
- table.AddRow([Header(3, Bold(
- FontAttr(_('Error: '), color='#ff0000', size='+2').Format() +
- Italic(errmsg).Format()))])
- table.AddRow([_("""You can create a new mailing list by entering the
- relevant information into the form below. The name of the mailing list
+ d['heading'] = _('Error: ') + '%s' % errmsg
+ d['instructions'] = _("""
'])
ftable.AddCellInfo(ftable.GetCurrentRowIndex(), 0, colspan=2)
ftable.AddRow([Label(_("List creator's (authentication) password:")),
PasswordBox('auth')])
- ftable.AddCellInfo(ftable.GetCurrentRowIndex(), 0, bgcolor=GREY)
- ftable.AddCellInfo(ftable.GetCurrentRowIndex(), 1, bgcolor=GREY)
+ ftable.AddCellInfo(ftable.GetCurrentRowIndex(), 0)
+ ftable.AddCellInfo(ftable.GetCurrentRowIndex(), 1)
ftable.AddRow([Center(SubmitButton('doit', _('Create List'))),
Center(SubmitButton('clear', _('Clear Form')))])
- form.AddItem(ftable)
- table.AddRow([form])
- doc.AddItem(table)
+
+ d['ftable'] = ftable.Format()
+
+ innertext = Utils.maketext("createlist.html", d)
+ return innertext
diff --git a/Mailman/Cgi/edithtml.py b/Mailman/Cgi/edithtml.py
index 0e34a1c..7585a51 100644
--- a/Mailman/Cgi/edithtml.py
+++ b/Mailman/Cgi/edithtml.py
@@ -42,7 +42,7 @@ def main():
return s
template_data = (
- ('listinfo.html', _('General list information page')),
+ ('mm-listinfo.html', _('General list information page')),
('subscribe.html', _('Subscribe results page')),
('options.html', _('User specific options page')),
('subscribeack.txt', _('Welcome email text file')),
diff --git a/Mailman/Cgi/listinfo.py b/Mailman/Cgi/listinfo.py
index abbf570..76921cf 100644
--- a/Mailman/Cgi/listinfo.py
+++ b/Mailman/Cgi/listinfo.py
@@ -23,6 +23,7 @@ import os
import cgi
from Mailman import mm_cfg
+from Mailman.HTMLFormatter import HTMLFormatter
from Mailman import Utils
from Mailman import MailList
from Mailman import Errors
@@ -67,16 +68,13 @@ def listinfo_overview(msg=''):
hostname = Utils.get_domain()
# Set up the document and assign it the correct language. The only one we
# know about at the moment is the server's default.
- doc = Document()
+ d = {}
+ doc = HeadlessDocument()
doc.set_language(mm_cfg.DEFAULT_SERVER_LANGUAGE)
- legend = _("%(hostname)s Mailing Lists")
- doc.SetTitle(legend)
+ d['title'] = _("%(hostname)s Mailing Lists")
- table = Table(border=0, width="100%")
- table.AddRow([Center(Header(2, legend))])
- table.AddCellInfo(table.GetCurrentRowIndex(), 0, colspan=2,
- bgcolor=mm_cfg.WEB_HEADER_COLOR)
+ d['heading'] = d['title']
# Skip any mailing lists that isn't advertised.
advertised = []
@@ -95,59 +93,73 @@ def listinfo_overview(msg=''):
mlist.real_name,
mlist.description))
if msg:
- greeting = FontAttr(msg, color="ff5060", size="+1")
+ d['greeting'] = msg
else:
- greeting = FontAttr(_('Welcome!'), size='+2')
+ d['greeting'] = _('Welcome!')
- welcome = [greeting]
+ welcome = []
mailmanlink = Link(mm_cfg.MAILMAN_URL, _('Mailman')).Format()
if not advertised:
welcome.extend(
_('''
')
- doc.AddItem(MailmanLogo())
+ rows.append(Utils.maketext("listinforow.html", d, mlist=mlist))
+
+ d['rows'] = ' '.join(rows)
+ d['mm_logo'] = MailmanLogo().Format()
+ d['header'] = ''
+ d['innertext'] = Utils.maketext("listinfo.html", d, mlist=mlist)
+ d['mailmanurl'] = Utils.ScriptURL('')
+ doc.AddItem(Utils.maketext("page.html", d, mlist=mlist))
print doc.Format()
@@ -200,7 +212,11 @@ def list_listinfo(mlist, lang):
replacements['
',
- Address(
+ return ' '
def FormatUsers(self, digest, lang=None, list_hidden=False):
if lang is None:
diff --git a/Mailman/MailList.py b/Mailman/MailList.py
index 674017d..7a6e09e 100644
--- a/Mailman/MailList.py
+++ b/Mailman/MailList.py
@@ -450,6 +450,36 @@ class MailList(HTMLFormatter, Deliverer, ListAdmin,
categories[k] = (v, gui)
return categories
+
+ #
+ # Web API support via administrative categories
+ #
+ def GetAdminLinks(self, categories):
+ class CategoryDict(UserDict):
+ def __init__(self):
+ UserDict.__init__(self)
+ self.categories = categories
+ self.keysinorder = categories[:]
+ def keys(self):
+ return self.keysinorder
+ def items(self):
+ items = []
+ for k in self.categories:
+ items.append((k, self.data[k]))
+ return items
+ def values(self):
+ values = []
+ for k in self.categories:
+ values.append(self.data[k])
+ return values
+
+ categories = CategoryDict()
+ # Only one level of mixin inheritance allowed
+ for gui in self._gui:
+ k, v = gui.GetConfigCategory()
+ categories[k] = (v, gui)
+ return categories
+
def GetConfigSubCategories(self, category):
for gui in self._gui:
if hasattr(gui, 'GetConfigSubCategories'):
diff --git a/Mailman/htmlformat.py b/Mailman/htmlformat.py
index 7152e1f..bf738ef 100644
--- a/Mailman/htmlformat.py
+++ b/Mailman/htmlformat.py
@@ -30,6 +30,7 @@ for python and, recursively, for nested HTML formatting objects.
import types
+import cgi
from Mailman import mm_cfg
from Mailman import Utils
from Mailman.i18n import _
@@ -206,18 +207,29 @@ class Table:
class Link:
- def __init__(self, href, text, target=None):
+ def __init__(self, href, text, target=None, id=None, classname=None):
self.href = href
self.text = text
self.target = target
+ self.id = id
+ self.classname = classname
def Format(self, indent=0):
texpr = ""
+ id = ""
+ classname = ""
if self.target != None:
texpr = ' target="%s"' % self.target
- return '%s' % (HTMLFormatObject(self.href, indent),
- texpr,
- HTMLFormatObject(self.text, indent))
+ if self.id:
+ id = ' id="%s" ' % self.id
+ if self.classname:
+ classname = ' class="%s" ' % self.classname
+ return '%s' % \
+ (id,
+ classname,
+ HTMLFormatObject(self.href, indent),
+ texpr,
+ HTMLFormatObject(self.text, indent))
class FontSize:
"""FontSize is being deprecated - use FontAttr(..., size="...") instead."""
@@ -305,18 +317,18 @@ class Document(Container):
kws.setdefault('bgcolor', self.bgcolor)
tab = ' ' * indent
output.extend([tab,
- '',
- ''
+ '',
+ ''
])
if mm_cfg.IMAGE_LOGOS:
- output.append('' %
+ output.append('' %
(mm_cfg.IMAGE_LOGOS + mm_cfg.SHORTCUT_ICON))
# Hit all the bases
- output.append('' % charset)
if self.title:
- output.append('%s