[gen] Pod field: more work on mailing lists.
This commit is contained in:
parent
f4ea1a5570
commit
e6f2b5213e
140
fields/pod.py
140
fields/pod.py
|
@ -26,13 +26,30 @@ from appy.pod import PodError
|
||||||
from appy.pod.renderer import Renderer
|
from appy.pod.renderer import Renderer
|
||||||
from appy.shared import utils as sutils
|
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):
|
class Pod(Field):
|
||||||
'''A pod is a field allowing to produce a (PDF, ODT, Word, RTF...) document
|
'''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
|
from data contained in Appy class and linked objects or anything you
|
||||||
want to put in it. It is the way gen uses pod.'''
|
want to put in it. It is the way gen uses pod.'''
|
||||||
# Layout for rendering a POD field for exporting query results.
|
# Some right-aligned layouts, convenient for pod fields exporting query
|
||||||
rLayouts = {'view': 'fl!'}
|
# 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')}
|
allFormats = {'.odt': ('pdf', 'doc', 'odt'), '.ods': ('xls', 'ods')}
|
||||||
|
|
||||||
POD_ERROR = 'An error occurred while generating the document. Please ' \
|
POD_ERROR = 'An error occurred while generating the document. Please ' \
|
||||||
|
@ -55,58 +72,72 @@ class Pod(Field):
|
||||||
|
|
||||||
pxView = pxCell = Px('''
|
pxView = pxCell = Px('''
|
||||||
<x var="uid=obj.uid"
|
<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"
|
<x for="fmt in info.formats"
|
||||||
var2="freezeAllowed=(fmt in info.freezeFormats) and \
|
var2="freezeAllowed=(fmt in info.freezeFormats) and \
|
||||||
(field.show != 'result');
|
(field.show != 'result');
|
||||||
|
hasMailings=mailings and (fmt in mailings);
|
||||||
|
dropdownEnabled=freezeAllowed or hasMailings;
|
||||||
frozen=field.isFrozen(obj, info.template, fmt)">
|
frozen=field.isFrozen(obj, info.template, fmt)">
|
||||||
<!-- A clickable icon if no freeze action is allowed -->
|
<!-- A clickable icon if no freeze action is allowed and no mailing is
|
||||||
<x if="not freezeAllowed">:field.pxIcon</x>
|
available for this format -->
|
||||||
|
<x if="not dropdownEnabled">:field.pxIcon</x>
|
||||||
<!-- A clickable icon and a dropdown menu else. -->
|
<!-- A clickable icon and a dropdown menu else. -->
|
||||||
<span if="freezeAllowed" class="dropdownMenu"
|
<span if="dropdownEnabled" class="dropdownMenu"
|
||||||
var2="dropdownId='%s_%s' % (uid, \
|
var2="dropdownId='%s_%s' % (uid, \
|
||||||
field.getFreezeName(info.template, fmt, sep='_'))"
|
field.getFreezeName(info.template, fmt, sep='_'))"
|
||||||
onmouseover=":'toggleDropdown(%s)' % q(dropdownId)"
|
onmouseover=":'toggleDropdown(%s)' % q(dropdownId)"
|
||||||
onmouseout=":'toggleDropdown(%s,%s)' % (q(dropdownId), q('none'))">
|
onmouseout=":'toggleDropdown(%s,%s)' % (q(dropdownId), q('none'))">
|
||||||
<x>:field.pxIcon</x>
|
<x>:field.pxIcon</x>
|
||||||
<!-- The dropdown menu containing freeze actions -->
|
<!-- The dropdown menu containing freeze actions -->
|
||||||
<table id=":dropdownId" class="dropdown" width="75px">
|
<table id=":dropdownId" class="dropdown" width="100px">
|
||||||
<!-- Unfreeze -->
|
<!-- Unfreeze -->
|
||||||
<tr if="frozen" valign="top">
|
<tr if="freezeAllowed and frozen" valign="top">
|
||||||
<td>
|
<td width="85px">
|
||||||
<a onclick=":'freezePod(%s,%s,%s,%s,%s)' % (q(uid), q(name), \
|
<a onclick=":'freezePod(%s,%s,%s,%s,%s)' % (q(uid), q(name), \
|
||||||
q(info.template), q(fmt), q('unfreeze'))"
|
q(info.template), q(fmt), q('unfreeze'))"
|
||||||
class="smaller">:_('unfreezeField')</a>
|
class="smaller">:_('unfreezeField')</a>
|
||||||
</td>
|
</td>
|
||||||
<td align="center"><img src=":url('unfreeze')"/></td>
|
<td width="15px"><img src=":url('unfreeze')"/></td>
|
||||||
</tr>
|
</tr>
|
||||||
<!-- (Re-)freeze -->
|
<!-- (Re-)freeze -->
|
||||||
<tr valign="top">
|
<tr if="freezeAllowed" valign="top">
|
||||||
<td>
|
<td width="85px">
|
||||||
<a onclick=":'freezePod(%s,%s,%s,%s,%s)' % (q(uid), q(name), \
|
<a onclick=":'freezePod(%s,%s,%s,%s,%s)' % (q(uid), q(name), \
|
||||||
q(info.template), q(fmt), q('freeze'))"
|
q(info.template), q(fmt), q('freeze'))"
|
||||||
class="smaller">:_('freezeField')</a>
|
class="smaller">:_('freezeField')</a>
|
||||||
</td>
|
</td>
|
||||||
<td align="center"><img src=":url('freeze')"/></td>
|
<td width="15px"><img src=":url('freeze')"/></td>
|
||||||
</tr>
|
</tr>
|
||||||
<!-- (Re-)upload -->
|
<!-- (Re-)upload -->
|
||||||
<tr valign="top">
|
<tr if="freezeAllowed" valign="top">
|
||||||
<td>
|
<td width="85px">
|
||||||
<a onclick=":'uploadPod(%s,%s,%s,%s)' % (q(uid), q(name), \
|
<a onclick=":'uploadPod(%s,%s,%s,%s)' % (q(uid), q(name), \
|
||||||
q(info.template), q(fmt))"
|
q(info.template), q(fmt))"
|
||||||
class="smaller">:_('uploadField')</a>
|
class="smaller">:_('uploadField')</a>
|
||||||
</td>
|
</td>
|
||||||
<td align="center"><img src=":url('upload')"/></td>
|
<td width="15px"><img src=":url('upload')"/></td>
|
||||||
</tr>
|
</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>
|
</table>
|
||||||
</span>
|
</span>
|
||||||
</x>
|
</x>
|
||||||
<!-- Show the specific template name only if there is more than one
|
<!-- Show the specific template name only if there is more than one
|
||||||
template. For a single template, the field label already does the
|
template. For a single template, the field label already does the
|
||||||
job. -->
|
job. -->
|
||||||
<span if="len(field.template) > 1"
|
<span if="len(field.template) > 1"
|
||||||
class=":not loop.info.last and 'pod smaller' or \
|
class=":(not loop.info.last and not lineBreak) and 'pod smaller' \
|
||||||
'smaller'">:field.getTemplateName(obj, info.template)</span>
|
or 'smaller'">:field.getTemplateName(obj, info.template)</span>
|
||||||
|
<br if="lineBreak"/>
|
||||||
</x>''')
|
</x>''')
|
||||||
|
|
||||||
pxEdit = pxSearch = ''
|
pxEdit = pxSearch = ''
|
||||||
|
@ -118,8 +149,9 @@ class Pod(Field):
|
||||||
maxChars=None, colspan=1, master=None, masterValue=None,
|
maxChars=None, colspan=1, master=None, masterValue=None,
|
||||||
focus=False, historized=False, mapping=None, label=None,
|
focus=False, historized=False, mapping=None, label=None,
|
||||||
template=None, templateName=None, showTemplate=None,
|
template=None, templateName=None, showTemplate=None,
|
||||||
freezeTemplate=None, context=None, stylesMapping={},
|
freezeTemplate=None, maxPerRow=5, context=None,
|
||||||
formats=None, getChecked=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
|
# 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
|
# a single template, a string is expected. Else, a list or tuple of
|
||||||
# strings is expected. Every such path must be relative to your
|
# 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
|
# - upload a document: the frozen or uploaded document will be replaced
|
||||||
# by a new document uploaded by the current user.
|
# by a new document uploaded by the current user.
|
||||||
self.freezeTemplate = freezeTemplate
|
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
|
# The context is a dict containing a specific pod context, or a method
|
||||||
# that returns such a dict.
|
# that returns such a dict.
|
||||||
self.context = context
|
self.context = context
|
||||||
|
@ -203,6 +238,33 @@ class Pod(Field):
|
||||||
# objects linked via the Ref field that are currently selected in the
|
# objects linked via the Ref field that are currently selected in the
|
||||||
# user interface.
|
# user interface.
|
||||||
self.getChecked = getChecked
|
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,
|
Field.__init__(self, None, (0,1), default, show, page, group, layouts,
|
||||||
move, indexed, searchable, specificReadPermission,
|
move, indexed, searchable, specificReadPermission,
|
||||||
specificWritePermission, width, height, None, colspan,
|
specificWritePermission, width, height, None, colspan,
|
||||||
|
@ -257,9 +319,9 @@ class Pod(Field):
|
||||||
'''Gets the name of a template given its p_fileName.'''
|
'''Gets the name of a template given its p_fileName.'''
|
||||||
res = None
|
res = None
|
||||||
if self.templateName:
|
if self.templateName:
|
||||||
# Use the method specified in self.templateName.
|
# Use the method specified in self.templateName
|
||||||
res = self.templateName(obj, fileName)
|
res = self.templateName(obj, fileName)
|
||||||
# Else, deduce a nice name from p_fileName.
|
# Else, deduce a nice name from p_fileName
|
||||||
if not res:
|
if not res:
|
||||||
name = os.path.splitext(os.path.basename(fileName))[0]
|
name = os.path.splitext(os.path.basename(fileName))[0]
|
||||||
res = gutils.produceNiceMessage(name)
|
res = gutils.produceNiceMessage(name)
|
||||||
|
@ -311,6 +373,38 @@ class Pod(Field):
|
||||||
freezeFormats=self.getFreezeFormats(obj, template)))
|
freezeFormats=self.getFreezeFormats(obj, template)))
|
||||||
return res
|
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,
|
def getValue(self, obj, template=None, format=None, result=None,
|
||||||
queryData=None, customContext=None, noSecurity=False):
|
queryData=None, customContext=None, noSecurity=False):
|
||||||
'''For a pod field, getting its value means computing a pod document or
|
'''For a pod field, getting its value means computing a pod document or
|
||||||
|
|
|
@ -718,3 +718,7 @@ msgstr ""
|
||||||
#. Default: "Microsoft Internet Explorer ${version} is not supported. Please upgrade your browser to version ${min} or above."
|
#. Default: "Microsoft Internet Explorer ${version} is not supported. Please upgrade your browser to version ${min} or above."
|
||||||
msgid "wrong_browser"
|
msgid "wrong_browser"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#. Default: "Send by email"
|
||||||
|
msgid "email_send"
|
||||||
|
msgstr ""
|
||||||
|
|
|
@ -718,3 +718,7 @@ msgstr ""
|
||||||
#. Default: "Microsoft Internet Explorer ${version} is not supported. Please upgrade your browser to version ${min} or above."
|
#. Default: "Microsoft Internet Explorer ${version} is not supported. Please upgrade your browser to version ${min} or above."
|
||||||
msgid "wrong_browser"
|
msgid "wrong_browser"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#. Default: "Send by email"
|
||||||
|
msgid "email_send"
|
||||||
|
msgstr ""
|
||||||
|
|
|
@ -718,3 +718,7 @@ msgstr ""
|
||||||
#. Default: "Microsoft Internet Explorer ${version} is not supported. Please upgrade your browser to version ${min} or above."
|
#. Default: "Microsoft Internet Explorer ${version} is not supported. Please upgrade your browser to version ${min} or above."
|
||||||
msgid "wrong_browser"
|
msgid "wrong_browser"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#. Default: "Send by email"
|
||||||
|
msgid "email_send"
|
||||||
|
msgstr ""
|
||||||
|
|
|
@ -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."
|
#. Default: "Microsoft Internet Explorer ${version} is not supported. Please upgrade your browser to version ${min} or above."
|
||||||
msgid "wrong_browser"
|
msgid "wrong_browser"
|
||||||
msgstr "Microsoft Internet Explorer ${version} is not supported. Please upgrade your browser to version ${min} or above."
|
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"
|
||||||
|
|
|
@ -718,3 +718,7 @@ msgstr ""
|
||||||
#. Default: "Microsoft Internet Explorer ${version} is not supported. Please upgrade your browser to version ${min} or above."
|
#. Default: "Microsoft Internet Explorer ${version} is not supported. Please upgrade your browser to version ${min} or above."
|
||||||
msgid "wrong_browser"
|
msgid "wrong_browser"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#. Default: "Send by email"
|
||||||
|
msgid "email_send"
|
||||||
|
msgstr ""
|
||||||
|
|
|
@ -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."
|
#. Default: "Microsoft Internet Explorer ${version} is not supported. Please upgrade your browser to version ${min} or above."
|
||||||
msgid "wrong_browser"
|
msgid "wrong_browser"
|
||||||
msgstr "Microsoft Internet Explorer ${version} n'est pas supporté. Veuillez mettre à jour votre navigateur à la version ${min} ou supérieure."
|
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"
|
||||||
|
|
|
@ -718,3 +718,7 @@ msgstr ""
|
||||||
#. Default: "Microsoft Internet Explorer ${version} is not supported. Please upgrade your browser to version ${min} or above."
|
#. Default: "Microsoft Internet Explorer ${version} is not supported. Please upgrade your browser to version ${min} or above."
|
||||||
msgid "wrong_browser"
|
msgid "wrong_browser"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#. Default: "Send by email"
|
||||||
|
msgid "email_send"
|
||||||
|
msgstr ""
|
||||||
|
|
|
@ -718,3 +718,7 @@ msgstr ""
|
||||||
#. Default: "Microsoft Internet Explorer ${version} is not supported. Please upgrade your browser to version ${min} or above."
|
#. Default: "Microsoft Internet Explorer ${version} is not supported. Please upgrade your browser to version ${min} or above."
|
||||||
msgid "wrong_browser"
|
msgid "wrong_browser"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#. Default: "Send by email"
|
||||||
|
msgid "email_send"
|
||||||
|
msgstr ""
|
||||||
|
|
|
@ -100,9 +100,10 @@ td.search { padding-top: 8px }
|
||||||
.popup { display: none; position: absolute; top: 30%; left: 35%;
|
.popup { display: none; position: absolute; top: 30%; left: 35%;
|
||||||
width: 350px; z-index : 100; background: white; padding: 8px;
|
width: 350px; z-index : 100; background: white; padding: 8px;
|
||||||
border: 1px solid grey; box-shadow: 2px 2px 2px #888888}
|
border: 1px solid grey; box-shadow: 2px 2px 2px #888888}
|
||||||
.dropdown { display:none; position: absolute; border: 1px solid #cccccc;
|
.dropdown { display:none; position: absolute; top: 15px; left: 1px;
|
||||||
background-color: white; padding: 3px 4px 0; font-size: 8pt;
|
border: 1px solid #cccccc; background-color: white;
|
||||||
font-weight: normal; text-align: left; z-index: 2 }
|
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 }
|
.dropdownMenu { cursor: pointer; font-size: 93%; position: relative }
|
||||||
.dropdown a:hover { text-decoration: underline }
|
.dropdown a:hover { text-decoration: underline }
|
||||||
.addForm { display: inline }
|
.addForm { display: inline }
|
||||||
|
@ -164,6 +165,7 @@ td.search { padding-top: 8px }
|
||||||
.error { margin: 5px }
|
.error { margin: 5px }
|
||||||
.smaller { font-size: 95% }
|
.smaller { font-size: 95% }
|
||||||
.pod { padding-right: 15px }
|
.pod { padding-right: 15px }
|
||||||
|
.podTable { line-height: 20px }
|
||||||
.cbCell { width: 10px; text-align: center}
|
.cbCell { width: 10px; text-align: center}
|
||||||
.tabs { position:relative; bottom:-2px }
|
.tabs { position:relative; bottom:-2px }
|
||||||
.tab { padding: 0 10px 0 10px; text-align: center; font-size: 90%;
|
.tab { padding: 0 10px 0 10px; text-align: center; font-size: 90%;
|
||||||
|
|
Loading…
Reference in a new issue