Added the possibility to define POD templates for any search result (Pod field with param view='search'), bugfix while getting default value for a Ref field, added Computed fields that computes a ZPT macro given as a string to param 'method', added the possibility to define a global style mapping for every Pod field, stopped to generate a field-specific set of i18n labels for pod output formats, carry portal_status_message even through page redirections, added 'deprecatedAddRemove' tags in generated configure.zcml, onEdit can now return a customized message, added possibility to normalize strings for other usages than 'fileName', in appy.shared.utils.normalizeString (for alpha and alphanum usages)

This commit is contained in:
Gaetan Delannay 2011-01-28 14:36:30 +01:00
parent 38f71be89a
commit 90553381a3
18 changed files with 250 additions and 59 deletions

View file

@ -45,6 +45,7 @@ class ToolMixin(BaseMixin):
res['title'] = self.translate(appyType.labelId)
res['context'] = appyType.context
res['action'] = appyType.action
res['stylesMapping'] = appyType.stylesMapping
return res
def getSiteUrl(self):
@ -68,7 +69,7 @@ class ToolMixin(BaseMixin):
template = podInfo['template'].content
podTitle = podInfo['title']
if podInfo['context']:
if type(podInfo['context']) == types.FunctionType:
if callable(podInfo['context']):
specificPodContext = podInfo['context'](appyObj)
else:
specificPodContext = podInfo['context']
@ -76,16 +77,38 @@ class ToolMixin(BaseMixin):
# Temporary file where to generate the result
tempFileName = '%s/%s_%f.%s' % (
getOsTempFolder(), obj.UID(), time.time(), format)
# Define parameters to pass to the appy.pod renderer
# Define parameters to give to the appy.pod renderer
currentUser = self.portal_membership.getAuthenticatedMember()
podContext = {'tool': appyTool, 'user': currentUser, 'self': appyObj,
'now': self.getProductConfig().DateTime(),
'projectFolder': appyTool.getDiskFolder(),
}
# If the POD document is related to a query, get it from the request,
# execute it and put the result in the context.
if rq['queryData']:
# Retrieve query params from the request
cmd = ', '.join(self.queryParamNames)
cmd += " = rq['queryData'].split(';')"
exec cmd
# (re-)execute the query, but without any limit on the number of
# results; return Appy objects.
objs = self.executeQuery(type_name, searchName=search,
sortBy=sortKey, sortOrder=sortOrder, filterKey=filterKey,
filterValue=filterValue, maxResults='NO_LIMIT')
podContext['objects'] = [o.appy() for o in objs['objects']]
if specificPodContext:
podContext.update(specificPodContext)
# Define a potential global styles mapping
stylesMapping = None
if podInfo['stylesMapping']:
if callable(podInfo['stylesMapping']):
stylesMapping = podInfo['stylesMapping'](appyObj)
else:
stylesMapping = podInfo['stylesMapping']
rendererParams = {'template': StringIO.StringIO(template),
'context': podContext, 'result': tempFileName}
if stylesMapping:
rendererParams['stylesMapping'] = stylesMapping
if appyTool.unoEnabledPython:
rendererParams['pythonWithUnoPath'] = appyTool.unoEnabledPython
if appyTool.openOfficePort:
@ -105,7 +128,13 @@ class ToolMixin(BaseMixin):
f = file(tempFileName, 'rb')
res = f.read()
# Identify the filename to return
fileName = u'%s-%s' % (obj.Title().decode('utf-8'), podTitle)
if rq['queryData']:
# This is a POD for a bunch of objects
fileName = podTitle
else:
# This is a POD for a single object: personalize the file name with
# the object title.
fileName = '%s-%s' % (obj.Title(), podTitle)
fileName = appyTool.normalize(fileName)
response = obj.REQUEST.RESPONSE
response.setHeader('Content-Type', mimeTypes[format])
@ -189,6 +218,19 @@ class ToolMixin(BaseMixin):
return {'fields': fields, 'nbOfColumns': nbOfColumns,
'fieldDicts': fieldDicts}
queryParamNames = ('type_name', 'search', 'sortKey', 'sortOrder',
'filterKey', 'filterValue')
def getQueryInfo(self):
'''If we are showing search results, this method encodes in a string all
the params in the request that are required for re-triggering the
search.'''
rq = self.REQUEST
res = ''
if rq.has_key('search'):
res = ';'.join([rq.get(key,'').replace(';','') \
for key in self.queryParamNames])
return res
def getImportElements(self, contentType):
'''Returns the list of elements that can be imported from p_path for
p_contentType.'''
@ -863,4 +905,10 @@ class ToolMixin(BaseMixin):
os.remove(fileName)
return content
return 'File does not exist'
def getResultPodFields(self, contentType):
'''Finds, among fields defined on p_contentType, which ones are Pod
fields that need to be shown on a page displaying query results.'''
return [f.__dict__ for f in self.getAllAppyTypes(contentType) \
if (f.type == 'Pod') and (f.show == 'result')]
# ------------------------------------------------------------------------------

View file

@ -3,7 +3,7 @@
- mixins/ToolMixin is mixed in with the generated application Tool class.'''
# ------------------------------------------------------------------------------
import os, os.path, sys, types, mimetypes
import os, os.path, sys, types, mimetypes, urllib
import appy.gen
from appy.gen import Type, String, Selection, Role
from appy.gen.utils import *
@ -67,13 +67,15 @@ class BaseMixin:
initiator.appy().link(fieldName, obj)
# Call the custom "onEdit" if available
msg = None # The message to display to the user. It can be set by onEdit
if obj.wrapperClass:
appyObject = obj.appy()
if hasattr(appyObject, 'onEdit'): appyObject.onEdit(created)
if hasattr(appyObject, 'onEdit'):
msg = appyObject.onEdit(created)
# Manage "add" permissions and reindex the object
obj._appy_managePermissions()
obj.reindexObject()
return obj
return obj, msg
def delete(self):
'''This methods is self's suicide.'''
@ -219,10 +221,21 @@ class BaseMixin:
return self.skyn.edit(self)
# Create or update the object in the database
obj = self.createOrUpdate(isNew, values)
obj, msg = self.createOrUpdate(isNew, values)
# Redirect the user to the appropriate page
msg = obj.translate('Changes saved.', domain='plone')
if not msg: msg = obj.translate('Changes saved.', domain='plone')
# If the object has already been deleted (ie, it is a kind of transient
# object like a one-shot form and has already been deleted in method
# onEdit), redirect to the main site page.
if not getattr(obj.getParentNode(), obj.id, None):
obj.unindexObject()
return self.goto(tool.getSiteUrl(), msg)
# If the user can't access the object anymore, redirect him to the
# main site page.
user = self.portal_membership.getAuthenticatedMember()
if not user.has_permission('View', obj):
return self.goto(tool.getSiteUrl(), msg)
if rq.get('buttonOk.x', None) or saveConfirmed:
# Go to the consult view for this object
obj.plone_utils.addPortalMessage(msg)
@ -321,8 +334,10 @@ class BaseMixin:
if previousData:
self.addDataChange(previousData)
def goto(self, url, addParams=False):
def goto(self, url, msg=None):
'''Brings the user to some p_url after an action has been executed.'''
if msg:
url += '?' + urllib.urlencode([('portal_status_message',msg)])
return self.REQUEST.RESPONSE.redirect(url)
def showField(self, name, layoutType='view'):