[gen] Pod field: more work on mailing lists.

This commit is contained in:
Gaetan Delannay 2014-09-17 14:22:36 +02:00
parent f4ea1a5570
commit e6f2b5213e
10 changed files with 154 additions and 26 deletions

View file

@ -26,13 +26,30 @@ from appy.pod import PodError
from appy.pod.renderer import Renderer
from appy.shared import utils as sutils
# ------------------------------------------------------------------------------
class Mailing:
'''Represents a mailing list as can be used by a pod field (see below).'''
def __init__(self, name=None, logins=None, subject=None, body=None):
# The mailing list name, as shown in the user interface
self.name = name
# The list of logins that will be used as recipients for sending
# emails.
self.logins = logins
# The mail subject
self.subject = subject
# The mail body
self.body = body
# ------------------------------------------------------------------------------
class Pod(Field):
'''A pod is a field allowing to produce a (PDF, ODT, Word, RTF...) document
from data contained in Appy class and linked objects or anything you
want to put in it. It is the way gen uses pod.'''
# Layout for rendering a POD field for exporting query results.
rLayouts = {'view': 'fl!'}
# Some right-aligned layouts, convenient for pod fields exporting query
# results or multi-template pod fields.
rLayouts = {'view': Table('fl!', css_class='podTable')} # "r"ight
# "r"ight "m"ulti-template (where the global field label is not used
rmLayouts = {'view': Table('f!', css_class='podTable')}
allFormats = {'.odt': ('pdf', 'doc', 'odt'), '.ods': ('xls', 'ods')}
POD_ERROR = 'An error occurred while generating the document. Please ' \
@ -55,58 +72,72 @@ class Pod(Field):
pxView = pxCell = Px('''
<x var="uid=obj.uid"
for="info in field.getVisibleTemplates(obj)">
for="info in field.getVisibleTemplates(obj)"
var2="mailings=field.getVisibleMailings(obj, info.template);
lineBreak=((loop.info.nb + 1) % field.maxPerRow) == 0">
<x for="fmt in info.formats"
var2="freezeAllowed=(fmt in info.freezeFormats) and \
(field.show != 'result');
hasMailings=mailings and (fmt in mailings);
dropdownEnabled=freezeAllowed or hasMailings;
frozen=field.isFrozen(obj, info.template, fmt)">
<!-- A clickable icon if no freeze action is allowed -->
<x if="not freezeAllowed">:field.pxIcon</x>
<!-- A clickable icon if no freeze action is allowed and no mailing is
available for this format -->
<x if="not dropdownEnabled">:field.pxIcon</x>
<!-- A clickable icon and a dropdown menu else. -->
<span if="freezeAllowed" class="dropdownMenu"
<span if="dropdownEnabled" class="dropdownMenu"
var2="dropdownId='%s_%s' % (uid, \
field.getFreezeName(info.template, fmt, sep='_'))"
onmouseover=":'toggleDropdown(%s)' % q(dropdownId)"
onmouseout=":'toggleDropdown(%s,%s)' % (q(dropdownId), q('none'))">
<x>:field.pxIcon</x>
<!-- The dropdown menu containing freeze actions -->
<table id=":dropdownId" class="dropdown" width="75px">
<table id=":dropdownId" class="dropdown" width="100px">
<!-- Unfreeze -->
<tr if="frozen" valign="top">
<td>
<tr if="freezeAllowed and frozen" valign="top">
<td width="85px">
<a onclick=":'freezePod(%s,%s,%s,%s,%s)' % (q(uid), q(name), \
q(info.template), q(fmt), q('unfreeze'))"
class="smaller">:_('unfreezeField')</a>
</td>
<td align="center"><img src=":url('unfreeze')"/></td>
<td width="15px"><img src=":url('unfreeze')"/></td>
</tr>
<!-- (Re-)freeze -->
<tr valign="top">
<td>
<tr if="freezeAllowed" valign="top">
<td width="85px">
<a onclick=":'freezePod(%s,%s,%s,%s,%s)' % (q(uid), q(name), \
q(info.template), q(fmt), q('freeze'))"
class="smaller">:_('freezeField')</a>
</td>
<td align="center"><img src=":url('freeze')"/></td>
<td width="15px"><img src=":url('freeze')"/></td>
</tr>
<!-- (Re-)upload -->
<tr valign="top">
<td>
<tr if="freezeAllowed" valign="top">
<td width="85px">
<a onclick=":'uploadPod(%s,%s,%s,%s)' % (q(uid), q(name), \
q(info.template), q(fmt))"
class="smaller">:_('uploadField')</a>
</td>
<td align="center"><img src=":url('upload')"/></td>
<td width="15px"><img src=":url('upload')"/></td>
</tr>
<!-- Mailing lists -->
<x if="hasMailings" var2="sendLabel=_('email_send')">
<tr for="mailing in mailings[fmt]" valign="top"
var2="mailingName=field.getMailingName(obj, mailing)">
<td width="85px"><span title=":sendLabel">:mailingName</span></td>
<td width="15px"><img src=":url('email')"/></td>
</tr>
</x>
</table>
</span>
</x>
</x>
<!-- Show the specific template name only if there is more than one
template. For a single template, the field label already does the
job. -->
<span if="len(field.template) &gt; 1"
class=":not loop.info.last and 'pod smaller' or \
'smaller'">:field.getTemplateName(obj, info.template)</span>
class=":(not loop.info.last and not lineBreak) and 'pod smaller' \
or 'smaller'">:field.getTemplateName(obj, info.template)</span>
<br if="lineBreak"/>
</x>''')
pxEdit = pxSearch = ''
@ -118,8 +149,9 @@ class Pod(Field):
maxChars=None, colspan=1, master=None, masterValue=None,
focus=False, historized=False, mapping=None, label=None,
template=None, templateName=None, showTemplate=None,
freezeTemplate=None, context=None, stylesMapping={},
formats=None, getChecked=None):
freezeTemplate=None, maxPerRow=5, context=None,
stylesMapping={}, formats=None, getChecked=None, mailing=None,
mailingName=None, showMailing=None, mailingInfo=None):
# Param "template" stores the path to the pod template(s). If there is
# a single template, a string is expected. Else, a list or tuple of
# strings is expected. Every such path must be relative to your
@ -186,6 +218,9 @@ class Pod(Field):
# - upload a document: the frozen or uploaded document will be replaced
# by a new document uploaded by the current user.
self.freezeTemplate = freezeTemplate
# If p_template contains more than 1 template, "maxPerRow" tells how
# much templates must appear side by side.
self.maxPerRow = maxPerRow
# The context is a dict containing a specific pod context, or a method
# that returns such a dict.
self.context = context
@ -203,6 +238,33 @@ class Pod(Field):
# objects linked via the Ref field that are currently selected in the
# user interface.
self.getChecked = getChecked
# Mailing lists can be defined for this pod field. For every visible
# mailing list, a menu item will be available in the user interface and
# will allow to send the pod result as attachment to the mailing list
# recipients. Attribute p_mailing stores a mailing list's id
# (as a string) or a list of ids.
self.mailing = mailing
if isinstance(mailing, basestring):
self.mailing = [mailing]
elif isinstance(mailing, tuple):
self.mailing = list(mailing)
# "mailingName" returns the name of the mailing as will be shown in the
# user interface. It must be a method accepting the mailing list id
# (from self.mailing) as single arg and returning the mailing list's
# name.
self.mailingName = mailingName
# "showMailing" below determines when the mailing list(s) must be shown.
# It may store a method accepting a mailing list's id (among
# self.mailing) and a template (among self.template) and returning the
# list or tuple of formats for which the pod result can be sent to the
# mailing list. If no such method is defined, the mailing list will be
# available for all visible templates and formats.
self.showMailing = showMailing
# When it it time to send an email, "mailingInfo" gives all the
# necessary information for this email: recipients, subject, body. It
# must be a method whose single arg is the mailing id (from
# self.mailing) and that returns an instance of class Mailing (above).
self.mailingInfo = mailingInfo
Field.__init__(self, None, (0,1), default, show, page, group, layouts,
move, indexed, searchable, specificReadPermission,
specificWritePermission, width, height, None, colspan,
@ -257,9 +319,9 @@ class Pod(Field):
'''Gets the name of a template given its p_fileName.'''
res = None
if self.templateName:
# Use the method specified in self.templateName.
# Use the method specified in self.templateName
res = self.templateName(obj, fileName)
# Else, deduce a nice name from p_fileName.
# Else, deduce a nice name from p_fileName
if not res:
name = os.path.splitext(os.path.basename(fileName))[0]
res = gutils.produceNiceMessage(name)
@ -311,6 +373,38 @@ class Pod(Field):
freezeFormats=self.getFreezeFormats(obj, template)))
return res
def getVisibleMailings(self, obj, template):
'''Gets, among self.mailing, the mailing(s) that can be shown for
p_template, as a dict ~{s_format:[s_id]}~.'''
if not self.mailing: return
res = {}
for mailing in self.mailing:
# Is this mailing visible ? In which format(s) ?
if not self.showMailing:
# By default, the mailing is available in any format
formats = True
else:
formats = self.showMailing(obj, mailing, template)
if not formats: continue
if isinstance(formats, bool): formats = self.formats
elif isinstance(formats, basestring): formats = (formats,)
# Add this mailing to the result
for fmt in formats:
if fmt in res: res[fmt].append(mailing)
else: res[fmt] = [mailing]
return res
def getMailingName(self, obj, mailing):
'''Gets the name of a particular p_mailing.'''
res = None
if self.mailingName:
# Use the method specified in self.mailingName
res = self.mailingName(obj, mailing)
if not res:
# Deduce a nice name from p_mailing
res = gutils.produceNiceMessage(mailing)
return res
def getValue(self, obj, template=None, format=None, result=None,
queryData=None, customContext=None, noSecurity=False):
'''For a pod field, getting its value means computing a pod document or

View file

@ -718,3 +718,7 @@ msgstr ""
#. Default: "Microsoft Internet Explorer ${version} is not supported. Please upgrade your browser to version ${min} or above."
msgid "wrong_browser"
msgstr ""
#. Default: "Send by email"
msgid "email_send"
msgstr ""

View file

@ -718,3 +718,7 @@ msgstr ""
#. Default: "Microsoft Internet Explorer ${version} is not supported. Please upgrade your browser to version ${min} or above."
msgid "wrong_browser"
msgstr ""
#. Default: "Send by email"
msgid "email_send"
msgstr ""

View file

@ -718,3 +718,7 @@ msgstr ""
#. Default: "Microsoft Internet Explorer ${version} is not supported. Please upgrade your browser to version ${min} or above."
msgid "wrong_browser"
msgstr ""
#. Default: "Send by email"
msgid "email_send"
msgstr ""

View file

@ -719,3 +719,7 @@ msgstr "You are not allowed to consult this."
#. Default: "Microsoft Internet Explorer ${version} is not supported. Please upgrade your browser to version ${min} or above."
msgid "wrong_browser"
msgstr "Microsoft Internet Explorer ${version} is not supported. Please upgrade your browser to version ${min} or above."
#. Default: "Send by email"
msgid "email_send"
msgstr "Send by email"

View file

@ -718,3 +718,7 @@ msgstr ""
#. Default: "Microsoft Internet Explorer ${version} is not supported. Please upgrade your browser to version ${min} or above."
msgid "wrong_browser"
msgstr ""
#. Default: "Send by email"
msgid "email_send"
msgstr ""

View file

@ -719,3 +719,7 @@ msgstr "Vous n'êtes pas autorisé à consulter ceci."
#. Default: "Microsoft Internet Explorer ${version} is not supported. Please upgrade your browser to version ${min} or above."
msgid "wrong_browser"
msgstr "Microsoft Internet Explorer ${version} n'est pas supporté. Veuillez mettre à jour votre navigateur à la version ${min} ou supérieure."
#. Default: "Send by email"
msgid "email_send"
msgstr "Envoyer par email"

View file

@ -718,3 +718,7 @@ msgstr ""
#. Default: "Microsoft Internet Explorer ${version} is not supported. Please upgrade your browser to version ${min} or above."
msgid "wrong_browser"
msgstr ""
#. Default: "Send by email"
msgid "email_send"
msgstr ""

View file

@ -718,3 +718,7 @@ msgstr ""
#. Default: "Microsoft Internet Explorer ${version} is not supported. Please upgrade your browser to version ${min} or above."
msgid "wrong_browser"
msgstr ""
#. Default: "Send by email"
msgid "email_send"
msgstr ""

View file

@ -100,9 +100,10 @@ td.search { padding-top: 8px }
.popup { display: none; position: absolute; top: 30%; left: 35%;
width: 350px; z-index : 100; background: white; padding: 8px;
border: 1px solid grey; box-shadow: 2px 2px 2px #888888}
.dropdown { display:none; position: absolute; border: 1px solid #cccccc;
background-color: white; padding: 3px 4px 0; font-size: 8pt;
font-weight: normal; text-align: left; z-index: 2 }
.dropdown { display:none; position: absolute; top: 15px; left: 1px;
border: 1px solid #cccccc; background-color: white;
padding: 3px 4px 0; font-size: 8pt; font-weight: normal;
text-align: left; z-index: 2; line-height: normal }
.dropdownMenu { cursor: pointer; font-size: 93%; position: relative }
.dropdown a:hover { text-decoration: underline }
.addForm { display: inline }
@ -164,6 +165,7 @@ td.search { padding-top: 8px }
.error { margin: 5px }
.smaller { font-size: 95% }
.pod { padding-right: 15px }
.podTable { line-height: 20px }
.cbCell { width: 10px; text-align: center}
.tabs { position:relative; bottom:-2px }
.tab { padding: 0 10px 0 10px; text-align: center; font-size: 90%;