From 4ebcfa287a2cbc8c0dfbf50cfc8261f8bc73839e Mon Sep 17 00:00:00 2001 From: Bombar Maxime Date: Sun, 17 May 2020 11:09:23 +0200 Subject: [PATCH] Huge clean up in mailman configuration --- logos/crans.png | Bin 0 -> 10618 bytes mailman.yml | 23 + roles/mailman/handlers/main.yml | 5 + roles/mailman/tasks/main.yml | 39 + .../mailman/templates/mailman/create.html.j2 | 13 + roles/mailman/templates/mailman/mm_cfg.py.j2 | 226 ++++++ .../templates/update-motd.d/05-mailman.j2 | 3 + .../usr/lib/mailman/Mailman/htmlformat.py.j2 | 742 ++++++++++++++++++ roles/nginx-mailman/handlers/main.yml | 5 + roles/nginx-mailman/tasks/main.yml | 43 + .../templates/nginx/mailman_passwd.j2 | 2 + .../nginx/sites-available/mailman.j2 | 94 +++ .../nginx/snippets/fastcgi-mailman.conf.j2 | 18 + .../nginx/snippets/fastcgi-mailman.conf.j2~ | 18 + .../nginx/snippets/options-ssl.conf.j2 | 17 + .../templates/update-motd.d/05-service.j2 | 3 + .../templates/var/www/custom_401.html.j2 | 18 + .../templates/var/www/robots.txt.j2 | 4 + 18 files changed, 1273 insertions(+) create mode 100644 logos/crans.png create mode 100755 mailman.yml create mode 100644 roles/mailman/handlers/main.yml create mode 100644 roles/mailman/tasks/main.yml create mode 100644 roles/mailman/templates/mailman/create.html.j2 create mode 100644 roles/mailman/templates/mailman/mm_cfg.py.j2 create mode 100755 roles/mailman/templates/update-motd.d/05-mailman.j2 create mode 100644 roles/mailman/templates/usr/lib/mailman/Mailman/htmlformat.py.j2 create mode 100644 roles/nginx-mailman/handlers/main.yml create mode 100644 roles/nginx-mailman/tasks/main.yml create mode 100644 roles/nginx-mailman/templates/nginx/mailman_passwd.j2 create mode 100644 roles/nginx-mailman/templates/nginx/sites-available/mailman.j2 create mode 100644 roles/nginx-mailman/templates/nginx/snippets/fastcgi-mailman.conf.j2 create mode 100644 roles/nginx-mailman/templates/nginx/snippets/fastcgi-mailman.conf.j2~ create mode 100644 roles/nginx-mailman/templates/nginx/snippets/options-ssl.conf.j2 create mode 100755 roles/nginx-mailman/templates/update-motd.d/05-service.j2 create mode 100644 roles/nginx-mailman/templates/var/www/custom_401.html.j2 create mode 100644 roles/nginx-mailman/templates/var/www/robots.txt.j2 diff --git a/logos/crans.png b/logos/crans.png new file mode 100644 index 0000000000000000000000000000000000000000..9c5e281a69694f2aed73c8466229c3dcb96b9609 GIT binary patch literal 10618 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 + + +Creation de mailing list + + + +

Creation de mailing list

+Il faut s'adresser a nounou arobase crans point org. + + diff --git a/roles/mailman/templates/mailman/mm_cfg.py.j2 b/roles/mailman/templates/mailman/mm_cfg.py.j2 new file mode 100644 index 00000000..25f82461 --- /dev/null +++ b/roles/mailman/templates/mailman/mm_cfg.py.j2 @@ -0,0 +1,226 @@ +{{ ansible_header | comment }} +# -*- python -*- + +# Copyright (C) 1998,1999,2000 by the Free Software Foundation, Inc. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA + + +"""This is the module which takes your site-specific settings. + +From a raw distribution it should be copied to mm_cfg.py. If you +already have an mm_cfg.py, be careful to add in only the new settings +you want. The complete set of distributed defaults, with annotation, +are in ./Defaults. In mm_cfg, override only those you want to +change, after the + + from Defaults import * + +line (see below). + +Note that these are just default settings - many can be overridden via the +admin and user interfaces on a per-list or per-user basis. + +Note also that some of the settings are resolved against the active list +setting by using the value as a format string against the +list-instance-object's dictionary - see the distributed value of +DEFAULT_MSG_FOOTER for an example.""" + + +####################################################### +# Here's where we get the distributed defaults. # + +from Defaults import * + + +##### +# General system-wide defaults +##### + +# Should image logos be used? Set this to 0 to disable image logos from "our +# sponsors" and just use textual links instead (this will also disable the +# shortcut "favicon"). Otherwise, this should contain the URL base path to +# the logo images (and must contain the trailing slash).. If you want to +# disable Mailman's logo footer altogther, hack +# Mailman/htmlformat.py:MailmanLogo(), which also contains the hardcoded links +# and image names. +IMAGE_LOGOS = '/images/mailman/' + +#------------------------------------------------------------- +# The name of the list Mailman uses to send password reminders +# and similar. Don't change if you want mailman-owner to be +# a valid local part. +MAILMAN_SITE_LIST = '{{ mailman.site_list }}' + +DEFAULT_URL= '{{ mailman.default_url }}' +DEFAULT_URL_PATTERN = 'https://%s/' +add_virtualhost(DEFAULT_URL_HOST, DEFAULT_EMAIL_HOST) + +#------------------------------------------------------------- +# Default domain for email addresses of newly created MLs +DEFAULT_EMAIL_HOST = '{{ mailman.default_host }}' +#------------------------------------------------------------- +# Default host for web interface of newly created MLs +DEFAULT_URL_HOST = '{{ mailman.default_host }}' +#------------------------------------------------------------- +# Required when setting any of its arguments. +add_virtualhost(DEFAULT_URL_HOST, DEFAULT_EMAIL_HOST) + +#------------------------------------------------------------- +# Do we send monthly reminders? +DEFAULT_SEND_REMINDERS = No + +# Normally when a site administrator authenticates to a web page with the site +# password, they get a cookie which authorizes them as the list admin. It +# makes me nervous to hand out site auth cookies because if this cookie is +# cracked or intercepted, the intruder will have access to every list on the +# site. OTOH, it's dang handy to not have to re-authenticate to every list on +# the site. Set this value to Yes to allow site admin cookies. +ALLOW_SITE_ADMIN_COOKIES = Yes + +##### +# Archive defaults +##### + +PUBLIC_ARCHIVE_URL = '{{ mailman.default_url }}archives/%(listname)s' + +# Are archives on or off by default? +DEFAULT_ARCHIVE = Off + +# Are archives public or private by default? +# 0=public, 1=private +DEFAULT_ARCHIVE_PRIVATE = 1 + +# Pipermail assumes that messages bodies contain US-ASCII text. +# Change this option to define a different character set to be used as +# the default character set for the archive. The term "character set" +# is used in MIME to refer to a method of converting a sequence of +# octets into a sequence of characters. If you change the default +# charset, you might need to add it to VERBATIM_ENCODING below. +DEFAULT_CHARSET = 'utf-8' + +# Most character set encodings require special HTML entity characters to be +# quoted, otherwise they won't look right in the Pipermail archives. However +# some character sets must not quote these characters so that they can be +# rendered properly in the browsers. The primary issue is multi-byte +# encodings where the octet 0x26 does not always represent the & character. +# This variable contains a list of such characters sets which are not +# HTML-quoted in the archives. +VERBATIM_ENCODING = ['utf-8'] + +##### +# General defaults +##### + +# The default language for this server. Whenever we can't figure out the list +# context or user context, we'll fall back to using this language. See +# LC_DESCRIPTIONS below for legal values. +DEFAULT_SERVER_LANGUAGE = '{{ mailman.default_language }}' + +# How many members to display at a time on the admin cgi to unsubscribe them +# or change their options? +DEFAULT_ADMIN_MEMBER_CHUNKSIZE = 50 + +# set this variable to Yes to allow list owners to delete their own mailing +# lists. You may not want to give them this power, in which case, setting +# this variable to No instead requires list removal to be done by the site +# administrator, via the command line script bin/rmlist. +#OWNERS_CAN_DELETE_THEIR_OWN_LISTS = No + +# Set this variable to Yes to allow list owners to set the "personalized" +# flags on their mailing lists. Turning these on tells Mailman to send +# separate email messages to each user instead of batching them together for +# delivery to the MTA. This gives each member a more personalized message, +# but can have a heavy impact on the performance of your system. +#OWNERS_CAN_ENABLE_PERSONALIZATION = No + +##### +# List defaults. NOTE: Changing these values does NOT change the +# configuration of an existing list. It only defines the default for new +# lists you subsequently create. +##### + +# Should a list, by default be advertised? What is the default maximum number +# of explicit recipients allowed? What is the default maximum message size +# allowed? +DEFAULT_LIST_ADVERTISED = Yes + +# {header-name: regexp} spam filtering - we include some for example sake. +DEFAULT_BOUNCE_MATCHING_HEADERS = """ +# Les lignes commencant par # sont des commentairtes. +#from: .*-owner@yahoogroups.com +#from: .*@uplinkpro.com +#from: .*@coolstats.comic.com +#from: .*@trafficmagnet.com +#from: .*@hotmail.com +#X-Reject: 450 +#X-Reject: 554 +""" + +# Mailman can be configured to strip any existing Reply-To: header, or simply +# extend any existing Reply-To: with one based on the above setting. +DEFAULT_FIRST_STRIP_REPLY_TO = Yes + +# SUBSCRIBE POLICY +# 0 - open list (only when ALLOW_OPEN_SUBSCRIBE is set to 1) ** +# 1 - confirmation required for subscribes +# 2 - admin approval required for subscribes +# 3 - both confirmation and admin approval required +# +# ** please do not choose option 0 if you are not allowing open +# subscribes (next variable) +DEFAULT_SUBSCRIBE_POLICY = 3 + +# Is the list owner notified of subscribes/unsubscribes? +DEFAULT_ADMIN_NOTIFY_MCHANGES = Yes + +# Do we send monthly reminders? +DEFAULT_SEND_REMINDERS = No + +# What should happen to non-member posts which do not match explicit +# non-member actions? +# 0 = Accept +# 1 = Hold +# 2 = Reject +# 3 = Discard +DEFAULT_GENERIC_NONMEMBER_ACTION = 1 + +# Use spamassassin automatically +GLOBAL_PIPELINE.insert(5, '{{ spamassassin }}') +# Discard messages with score higher than ... +SPAMASSASSIN_DISCARD_SCORE = 8 +# Hold in moderation messages with score higher than ... +SPAMASSASSIN_HOLD_SCORE = 2.1 + +# Add SpamAssassin administration interface on gui +# To make it work, you need to edit Gui/__init__.py +# with +# from SpamAssassin import SpamAssassin +ADMIN_CATEGORIES.append("spamassassin") + +# Add header to keep +PLAIN_DIGEST_KEEP_HEADERS.append('X-Spam-Score') + +# configure MTA +MTA = 'Postfix' +SMTPHOST = '{{ smtphost }}' +SMTP_MAX_RCPTS = 50 + + +POSTFIX_STYLE_VIRTUAL_DOMAINS = ["{{ mailman.default_host }}"] + +# Note - if you're looking for something that is imported from mm_cfg, but you +# didn't find it above, it's probably in /usr/lib/mailman/Mailman/Defaults.py. diff --git a/roles/mailman/templates/update-motd.d/05-mailman.j2 b/roles/mailman/templates/update-motd.d/05-mailman.j2 new file mode 100755 index 00000000..d3fee0db --- /dev/null +++ b/roles/mailman/templates/update-motd.d/05-mailman.j2 @@ -0,0 +1,3 @@ +#!/usr/bin/tail +14 +{{ ansible_header | comment }} +> Mailman a été déployé sur cette machine. Voir /etc/mailman/ et /var/lib/mailman/. diff --git a/roles/mailman/templates/usr/lib/mailman/Mailman/htmlformat.py.j2 b/roles/mailman/templates/usr/lib/mailman/Mailman/htmlformat.py.j2 new file mode 100644 index 00000000..146f9576 --- /dev/null +++ b/roles/mailman/templates/usr/lib/mailman/Mailman/htmlformat.py.j2 @@ -0,0 +1,742 @@ +{{ ansible_header | comment }} +# Copyright (C) 1998-2018 by the Free Software Foundation, Inc. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, +# USA. + + +"""Library for program-based construction of an HTML documents. + +Encapsulate HTML formatting directives in classes that act as containers +for python and, recursively, for nested HTML formatting objects. +""" + + +# Eventually could abstract down to HtmlItem, which outputs an arbitrary html +# object given start / end tags, valid options, and a value. Ug, objects +# shouldn't be adding their own newlines. The next object should. + + +import types + +from Mailman import mm_cfg +from Mailman import Utils +from Mailman.i18n import _, get_translation + +from Mailman.CSRFcheck import csrf_token + +SPACE = ' ' +EMPTYSTRING = '' +NL = '\n' + + + +# Format an arbitrary object. +def HTMLFormatObject(item, indent): + "Return a presentation of an object, invoking their Format method if any." + if type(item) == type(''): + return item + elif not hasattr(item, "Format"): + return `item` + else: + return item.Format(indent) + +def CaseInsensitiveKeyedDict(d): + result = {} + for (k,v) in d.items(): + result[k.lower()] = v + return result + +# Given references to two dictionaries, copy the second dictionary into the +# first one. +def DictMerge(destination, fresh_dict): + for (key, value) in fresh_dict.items(): + destination[key] = value + +class Table: + def __init__(self, **table_opts): + self.cells = [] + self.cell_info = {} + self.row_info = {} + self.opts = table_opts + + def AddOptions(self, opts): + DictMerge(self.opts, opts) + + # Sets all of the cells. It writes over whatever cells you had there + # previously. + + def SetAllCells(self, cells): + self.cells = cells + + # Add a new blank row at the end + def NewRow(self): + self.cells.append([]) + + # Add a new blank cell at the end + def NewCell(self): + self.cells[-1].append('') + + def AddRow(self, row): + self.cells.append(row) + + def AddCell(self, cell): + self.cells[-1].append(cell) + + def AddCellInfo(self, row, col, **kws): + kws = CaseInsensitiveKeyedDict(kws) + if not self.cell_info.has_key(row): + self.cell_info[row] = { col : kws } + elif self.cell_info[row].has_key(col): + DictMerge(self.cell_info[row], kws) + else: + self.cell_info[row][col] = kws + + def AddRowInfo(self, row, **kws): + kws = CaseInsensitiveKeyedDict(kws) + if not self.row_info.has_key(row): + self.row_info[row] = kws + else: + DictMerge(self.row_info[row], kws) + + # What's the index for the row we just put in? + def GetCurrentRowIndex(self): + return len(self.cells)-1 + + # What's the index for the col we just put in? + def GetCurrentCellIndex(self): + return len(self.cells[-1])-1 + + def ExtractCellInfo(self, info): + valid_mods = ['align', 'valign', 'nowrap', 'rowspan', 'colspan', + 'bgcolor'] + output = '' + + for (key, val) in info.items(): + if not key in valid_mods: + continue + if key == 'nowrap': + output = output + ' NOWRAP' + continue + else: + output = output + ' %s="%s"' % (key.upper(), val) + + return output + + def ExtractRowInfo(self, info): + valid_mods = ['align', 'valign', 'bgcolor'] + output = '' + + for (key, val) in info.items(): + if not key in valid_mods: + continue + output = output + ' %s="%s"' % (key.upper(), val) + + return output + + def ExtractTableInfo(self, info): + valid_mods = ['align', 'width', 'border', 'cellspacing', 'cellpadding', + 'bgcolor'] + + output = '' + + for (key, val) in info.items(): + if not key in valid_mods: + continue + if key == 'border' and val == None: + output = output + ' BORDER' + continue + else: + output = output + ' %s="%s"' % (key.upper(), val) + + return output + + def FormatCell(self, row, col, indent): + try: + my_info = self.cell_info[row][col] + except: + my_info = None + + output = '\n' + ' '*indent + '' + + for i in range(len(self.cells[row])): + output = output + self.FormatCell(row, i, indent + 2) + + output = output + '\n' + ' '*indent + '' + + return output + + def Format(self, indent=0): + output = '\n' + ' '*indent + '' + + for i in range(len(self.cells)): + output = output + self.FormatRow(i, indent + 2) + + output = output + '\n' + ' '*indent + '\n' + + return output + + +class Link: + def __init__(self, href, text, target=None): + self.href = href + self.text = text + self.target = target + + def Format(self, indent=0): + texpr = "" + if self.target != None: + texpr = ' target="%s"' % self.target + return '%s' % (HTMLFormatObject(self.href, indent), + texpr, + HTMLFormatObject(self.text, indent)) + +class FontSize: + """FontSize is being deprecated - use FontAttr(..., size="...") instead.""" + def __init__(self, size, *items): + self.items = list(items) + self.size = size + + def Format(self, indent=0): + output = '' % self.size + for item in self.items: + output = output + HTMLFormatObject(item, indent) + output = output + '' + return output + +class FontAttr: + """Present arbitrary font attributes.""" + def __init__(self, *items, **kw): + self.items = list(items) + self.attrs = kw + + def Format(self, indent=0): + seq = [] + for k, v in self.attrs.items(): + seq.append('%s="%s"' % (k, v)) + output = '' % SPACE.join(seq) + for item in self.items: + output = output + HTMLFormatObject(item, indent) + output = output + '' + return output + + +class Container: + def __init__(self, *items): + if not items: + self.items = [] + else: + self.items = items + + def AddItem(self, obj): + self.items.append(obj) + + def Format(self, indent=0): + output = [] + for item in self.items: + output.append(HTMLFormatObject(item, indent)) + return EMPTYSTRING.join(output) + + +class Label(Container): + align = 'right' + + def __init__(self, *items): + Container.__init__(self, *items) + + def Format(self, indent=0): + return ('
' % self.align) + \ + Container.Format(self, indent) + \ + '
' + + +# My own standard document template. YMMV. +# something more abstract would be more work to use... + +class Document(Container): + title = None + language = None + bgcolor = mm_cfg.WEB_BG_COLOR + suppress_head = 0 + + def set_language(self, lang=None): + self.language = lang + + def set_bgcolor(self, color): + self.bgcolor = color + + def SetTitle(self, title): + self.title = title + + def Format(self, indent=0, **kws): + charset = 'us-ascii' + if self.language and Utils.IsLanguage(self.language): + charset = Utils.GetCharSet(self.language) + output = ['Content-Type: text/html; charset=%s' % charset] + output.append('Cache-control: no-cache\n') + if not self.suppress_head: + kws.setdefault('bgcolor', self.bgcolor) + tab = ' ' * indent + output.extend([tab, + '', + '' + ]) + if mm_cfg.IMAGE_LOGOS: + output.append('' % + (mm_cfg.IMAGE_LOGOS + mm_cfg.SHORTCUT_ICON)) + # Hit all the bases + output.append('' % charset) + if self.title: + output.append('%s%s' % (tab, self.title)) + # Add CSS to visually hide some labeling text but allow screen + # readers to read it. + output.append("""\ + +""") + if mm_cfg.WEB_HEAD_ADD: + output.append(mm_cfg.WEB_HEAD_ADD) + output.append('%s' % tab) + quals = [] + # Default link colors + if mm_cfg.WEB_VLINK_COLOR: + kws.setdefault('vlink', mm_cfg.WEB_VLINK_COLOR) + if mm_cfg.WEB_ALINK_COLOR: + kws.setdefault('alink', mm_cfg.WEB_ALINK_COLOR) + if mm_cfg.WEB_LINK_COLOR: + kws.setdefault('link', mm_cfg.WEB_LINK_COLOR) + for k, v in kws.items(): + quals.append('%s="%s"' % (k, v)) + output.append('%s' % direction) + # Always do this... + output.append(Container.Format(self, indent)) + if not self.suppress_head: + output.append('%s' % tab) + output.append('%s' % tab) + return NL.join(output) + + def addError(self, errmsg, tag=None): + if tag is None: + tag = _('Error: ') + self.AddItem(Header(3, Bold(FontAttr( + _(tag), color=mm_cfg.WEB_ERROR_COLOR, size='+2')).Format() + + Italic(errmsg).Format())) + + +class HeadlessDocument(Document): + """Document without head section, for templates that provide their own.""" + suppress_head = 1 + + +class StdContainer(Container): + def Format(self, indent=0): + # If I don't start a new I ignore indent + output = '<%s>' % self.tag + output = output + Container.Format(self, indent) + output = '%s' % (output, self.tag) + return output + + +class QuotedContainer(Container): + def Format(self, indent=0): + # If I don't start a new I ignore indent + output = '<%s>%s' % ( + self.tag, + Utils.websafe(Container.Format(self, indent)), + self.tag) + return output + +class Header(StdContainer): + def __init__(self, num, *items): + self.items = items + self.tag = 'h%d' % num + +class Address(StdContainer): + tag = 'address' + +class Underline(StdContainer): + tag = 'u' + +class Bold(StdContainer): + tag = 'strong' + +class Italic(StdContainer): + tag = 'em' + +class Preformatted(QuotedContainer): + tag = 'pre' + +class Subscript(StdContainer): + tag = 'sub' + +class Superscript(StdContainer): + tag = 'sup' + +class Strikeout(StdContainer): + tag = 'strike' + +class Center(StdContainer): + tag = 'center' + +class Form(Container): + def __init__(self, action='', method='POST', encoding=None, + mlist=None, contexts=None, user=None, *items): + apply(Container.__init__, (self,) + items) + self.action = action + self.method = method + self.encoding = encoding + self.mlist = mlist + self.contexts = contexts + self.user = user + + def set_action(self, action): + self.action = action + + def Format(self, indent=0): + spaces = ' ' * indent + encoding = '' + if self.encoding: + encoding = 'enctype="%s"' % self.encoding + output = '\n%s
\n' % ( + spaces, self.action, self.method, encoding) + if self.mlist: + output = output + \ + '\n' \ + % csrf_token(self.mlist, self.contexts, self.user) + output = output + Container.Format(self, indent+2) + output = '%s\n%s
\n' % (output, spaces) + return output + + +class InputObj: + def __init__(self, name, ty, value, checked, **kws): + self.name = name + self.type = ty + self.value = value + self.checked = checked + self.kws = kws + + def Format(self, indent=0): + charset = get_translation().charset() or 'us-ascii' + output = ['') + ret = SPACE.join(output) + if self.type == 'TEXT' and isinstance(ret, unicode): + ret = ret.encode(charset, 'xmlcharrefreplace') + return ret + + +class SubmitButton(InputObj): + def __init__(self, name, button_text): + InputObj.__init__(self, name, "SUBMIT", button_text, checked=0) + +class PasswordBox(InputObj): + def __init__(self, name, value='', size=mm_cfg.TEXTFIELDWIDTH): + InputObj.__init__(self, name, "PASSWORD", value, checked=0, size=size) + +class TextBox(InputObj): + def __init__(self, name, value='', size=mm_cfg.TEXTFIELDWIDTH): + if isinstance(value, str): + safevalue = Utils.websafe(value) + else: + safevalue = value + InputObj.__init__(self, name, "TEXT", safevalue, checked=0, size=size) + +class Hidden(InputObj): + def __init__(self, name, value=''): + InputObj.__init__(self, name, 'HIDDEN', value, checked=0) + +class TextArea: + def __init__(self, name, text='', rows=None, cols=None, wrap='soft', + readonly=0): + if isinstance(text, str): + # Double escape HTML entities in non-readonly areas. + doubleescape = not readonly + safetext = Utils.websafe(text, doubleescape) + else: + safetext = text + self.name = name + self.text = safetext + self.rows = rows + self.cols = cols + self.wrap = wrap + self.readonly = readonly + + def Format(self, indent=0): + charset = get_translation().charset() or 'us-ascii' + output = '