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:
parent
38f71be89a
commit
90553381a3
|
@ -506,7 +506,8 @@ class Type:
|
||||||
search results (p_usage="search") or when sorting reference fields
|
search results (p_usage="search") or when sorting reference fields
|
||||||
(p_usage="ref")?'''
|
(p_usage="ref")?'''
|
||||||
if usage == 'search':
|
if usage == 'search':
|
||||||
return self.indexed and not self.isMultiValued()
|
return self.indexed and not self.isMultiValued() and not \
|
||||||
|
((self.type == 'String') and self.isSelection())
|
||||||
elif usage == 'ref':
|
elif usage == 'ref':
|
||||||
return self.type in ('Integer', 'Float', 'Boolean', 'Date') or \
|
return self.type in ('Integer', 'Float', 'Boolean', 'Date') or \
|
||||||
((self.type == 'String') and (self.format == 0))
|
((self.type == 'String') and (self.format == 0))
|
||||||
|
@ -514,7 +515,6 @@ class Type:
|
||||||
def isShowable(self, obj, layoutType):
|
def isShowable(self, obj, layoutType):
|
||||||
'''When displaying p_obj on a given p_layoutType, must we show this
|
'''When displaying p_obj on a given p_layoutType, must we show this
|
||||||
field?'''
|
field?'''
|
||||||
isEdit = layoutType == 'edit'
|
|
||||||
# Do not show field if it is optional and not selected in tool
|
# Do not show field if it is optional and not selected in tool
|
||||||
if self.optional:
|
if self.optional:
|
||||||
tool = obj.getTool().appy()
|
tool = obj.getTool().appy()
|
||||||
|
@ -524,7 +524,7 @@ class Type:
|
||||||
return False
|
return False
|
||||||
# Check if the user has the permission to view or edit the field
|
# Check if the user has the permission to view or edit the field
|
||||||
user = obj.portal_membership.getAuthenticatedMember()
|
user = obj.portal_membership.getAuthenticatedMember()
|
||||||
if isEdit:
|
if layoutType == 'edit':
|
||||||
perm = self.writePermission
|
perm = self.writePermission
|
||||||
else:
|
else:
|
||||||
perm = self.readPermission
|
perm = self.readPermission
|
||||||
|
@ -535,14 +535,9 @@ class Type:
|
||||||
res = self.callMethod(obj, self.show)
|
res = self.callMethod(obj, self.show)
|
||||||
else:
|
else:
|
||||||
res = self.show
|
res = self.show
|
||||||
# Take into account possible values 'view' and 'edit' for 'show' param.
|
# Take into account possible values 'view', 'edit', 'search'...
|
||||||
if res == 'view':
|
if res in ('view', 'edit', 'result'): return res == layoutType
|
||||||
if isEdit: res = False
|
return bool(res)
|
||||||
else: res = True
|
|
||||||
elif res == 'edit':
|
|
||||||
if isEdit: res = True
|
|
||||||
else: res = False
|
|
||||||
return res
|
|
||||||
|
|
||||||
def isClientVisible(self, obj):
|
def isClientVisible(self, obj):
|
||||||
'''This method returns True if this field is visible according to
|
'''This method returns True if this field is visible according to
|
||||||
|
@ -1641,6 +1636,16 @@ class Ref(Type):
|
||||||
toDelete.append(uid)
|
toDelete.append(uid)
|
||||||
for uid in toDelete:
|
for uid in toDelete:
|
||||||
uids.remove(uid)
|
uids.remove(uid)
|
||||||
|
if not uids:
|
||||||
|
# Maybe is there a default value?
|
||||||
|
defValue = Type.getValue(self, obj)
|
||||||
|
if defValue:
|
||||||
|
# I must prefix call to function "type" with "__builtins__"
|
||||||
|
# because this name was overridden by a method parameter.
|
||||||
|
if __builtins__['type'](defValue) in sequenceTypes:
|
||||||
|
uids = [o.o.UID() for o in defValue]
|
||||||
|
else:
|
||||||
|
uids = [defValue.o.UID()]
|
||||||
# Prepare the result: an instance of SomeObjects, that, in this case,
|
# Prepare the result: an instance of SomeObjects, that, in this case,
|
||||||
# represent a subset of all referred objects
|
# represent a subset of all referred objects
|
||||||
res = SomeObjects()
|
res = SomeObjects()
|
||||||
|
@ -1751,6 +1756,10 @@ class Computed(Type):
|
||||||
self.method = method
|
self.method = method
|
||||||
# Does field computation produce plain text or XHTML?
|
# Does field computation produce plain text or XHTML?
|
||||||
self.plainText = plainText
|
self.plainText = plainText
|
||||||
|
if isinstance(method, basestring):
|
||||||
|
# When field computation is done with a macro, we know the result
|
||||||
|
# will be HTML.
|
||||||
|
self.plainText = False
|
||||||
Type.__init__(self, None, multiplicity, index, default, optional,
|
Type.__init__(self, None, multiplicity, index, default, optional,
|
||||||
False, show, page, group, layouts, move, indexed, False,
|
False, show, page, group, layouts, move, indexed, False,
|
||||||
specificReadPermission, specificWritePermission, width,
|
specificReadPermission, specificWritePermission, width,
|
||||||
|
@ -1758,10 +1767,30 @@ class Computed(Type):
|
||||||
sync)
|
sync)
|
||||||
self.validable = False
|
self.validable = False
|
||||||
|
|
||||||
|
def callMacro(self, obj, macroPath):
|
||||||
|
'''Returns the macro corresponding to p_macroPath. The base folder
|
||||||
|
where we search is "skyn".'''
|
||||||
|
# Get the special page in Appy that allows to call a macro
|
||||||
|
macroPage = obj.skyn.callMacro
|
||||||
|
# Get, from p_macroPath, the page where the macro lies, and the macro
|
||||||
|
# name.
|
||||||
|
names = self.method.split('/')
|
||||||
|
# Get the page where the macro lies
|
||||||
|
page = obj.skyn
|
||||||
|
for name in names[:-1]:
|
||||||
|
page = getattr(page, name)
|
||||||
|
macroName = names[-1]
|
||||||
|
return macroPage(obj, contextObj=obj, page=page, macroName=macroName)
|
||||||
|
|
||||||
def getValue(self, obj):
|
def getValue(self, obj):
|
||||||
'''Computes the value instead of getting it in the database.'''
|
'''Computes the value instead of getting it in the database.'''
|
||||||
if not self.method: return
|
if not self.method: return
|
||||||
return self.callMethod(obj, self.method, raiseOnError=False)
|
if isinstance(self.method, basestring):
|
||||||
|
# self.method is a path to a macro that will produce the field value
|
||||||
|
return self.callMacro(obj, self.method)
|
||||||
|
else:
|
||||||
|
# self.method is a method that will return the field value
|
||||||
|
return self.callMethod(obj, self.method, raiseOnError=False)
|
||||||
|
|
||||||
def getFormattedValue(self, obj, value):
|
def getFormattedValue(self, obj, value):
|
||||||
if not isinstance(value, basestring): return str(value)
|
if not isinstance(value, basestring): return str(value)
|
||||||
|
@ -1864,7 +1893,7 @@ class Pod(Type):
|
||||||
specificWritePermission=False, width=None, height=None,
|
specificWritePermission=False, width=None, height=None,
|
||||||
colspan=1, master=None, masterValue=None, focus=False,
|
colspan=1, master=None, masterValue=None, focus=False,
|
||||||
historized=False, template=None, context=None, action=None,
|
historized=False, template=None, context=None, action=None,
|
||||||
askAction=False):
|
askAction=False, stylesMapping=None):
|
||||||
# The following param stores the path to a POD template
|
# The following param stores the path to a POD template
|
||||||
self.template = template
|
self.template = template
|
||||||
# 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
|
||||||
|
@ -1876,6 +1905,8 @@ class Pod(Type):
|
||||||
# If askAction is True, the action will be triggered only if the user
|
# If askAction is True, the action will be triggered only if the user
|
||||||
# checks a checkbox, which, by default, will be unchecked.
|
# checks a checkbox, which, by default, will be unchecked.
|
||||||
self.askAction = askAction
|
self.askAction = askAction
|
||||||
|
# A global styles mapping that would apply to the whole template
|
||||||
|
self.stylesMapping = stylesMapping
|
||||||
Type.__init__(self, None, (0,1), index, default, optional,
|
Type.__init__(self, None, (0,1), index, default, optional,
|
||||||
False, show, page, group, layouts, move, indexed,
|
False, show, page, group, layouts, move, indexed,
|
||||||
searchable, specificReadPermission,
|
searchable, specificReadPermission,
|
||||||
|
|
|
@ -434,7 +434,7 @@ class ToolClassDescriptor(ClassDescriptor):
|
||||||
self.addField(fieldName, fieldType)
|
self.addField(fieldName, fieldType)
|
||||||
# Add the field that will store the output format(s)
|
# Add the field that will store the output format(s)
|
||||||
fieldName = 'formatsFor%s_%s' % (className, fieldDescr.fieldName)
|
fieldName = 'formatsFor%s_%s' % (className, fieldDescr.fieldName)
|
||||||
fieldType = String(validator=('odt', 'pdf', 'doc', 'rtf'),
|
fieldType = String(validator=Selection('getPodOutputFormats'),
|
||||||
multiplicity=(1,None), default=('odt',), **pg)
|
multiplicity=(1,None), default=('odt',), **pg)
|
||||||
self.addField(fieldName, fieldType)
|
self.addField(fieldName, fieldType)
|
||||||
|
|
||||||
|
|
|
@ -18,8 +18,14 @@ COMMON_METHODS = '''
|
||||||
def getTool(self): return self.%s
|
def getTool(self): return self.%s
|
||||||
def getProductConfig(self): return Products.%s.config
|
def getProductConfig(self): return Products.%s.config
|
||||||
def skynView(self):
|
def skynView(self):
|
||||||
"""Redirects to skyn/view."""
|
"""Redirects to skyn/view. Transfers the status message if any."""
|
||||||
return self.REQUEST.RESPONSE.redirect(self.getUrl())
|
rq = self.REQUEST
|
||||||
|
msg = rq.get('portal_status_message', '')
|
||||||
|
if msg:
|
||||||
|
url = self.getUrl(portal_status_message=msg)
|
||||||
|
else:
|
||||||
|
url = self.getUrl()
|
||||||
|
return rq.RESPONSE.redirect(url)
|
||||||
'''
|
'''
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
class Generator(AbstractGenerator):
|
class Generator(AbstractGenerator):
|
||||||
|
@ -140,6 +146,10 @@ class Generator(AbstractGenerator):
|
||||||
msg('field_invalid', '', msg.FIELD_INVALID),
|
msg('field_invalid', '', msg.FIELD_INVALID),
|
||||||
msg('file_required', '', msg.FILE_REQUIRED),
|
msg('file_required', '', msg.FILE_REQUIRED),
|
||||||
msg('image_required', '', msg.IMAGE_REQUIRED),
|
msg('image_required', '', msg.IMAGE_REQUIRED),
|
||||||
|
msg('odt', '', msg.FORMAT_ODT),
|
||||||
|
msg('pdf', '', msg.FORMAT_PDF),
|
||||||
|
msg('doc', '', msg.FORMAT_DOC),
|
||||||
|
msg('rtf', '', msg.FORMAT_RTF),
|
||||||
]
|
]
|
||||||
# Create a label for every role added by this application
|
# Create a label for every role added by this application
|
||||||
for role in self.getAllUsedRoles():
|
for role in self.getAllUsedRoles():
|
||||||
|
@ -153,7 +163,7 @@ class Generator(AbstractGenerator):
|
||||||
if self.config.frontPage:
|
if self.config.frontPage:
|
||||||
self.generateFrontPage()
|
self.generateFrontPage()
|
||||||
self.copyFile('Install.py', self.repls, destFolder='Extensions')
|
self.copyFile('Install.py', self.repls, destFolder='Extensions')
|
||||||
self.copyFile('configure.zcml', self.repls)
|
self.generateConfigureZcml()
|
||||||
self.copyFile('import_steps.xml', self.repls,
|
self.copyFile('import_steps.xml', self.repls,
|
||||||
destFolder='profiles/default')
|
destFolder='profiles/default')
|
||||||
self.copyFile('ProfileInit.py', self.repls, destFolder='profiles',
|
self.copyFile('ProfileInit.py', self.repls, destFolder='profiles',
|
||||||
|
@ -304,6 +314,17 @@ class Generator(AbstractGenerator):
|
||||||
if isBack: res += '.back'
|
if isBack: res += '.back'
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
def generateConfigureZcml(self):
|
||||||
|
'''Generates file configure.zcml.'''
|
||||||
|
repls = self.repls.copy()
|
||||||
|
# Note every class as "deprecated".
|
||||||
|
depr = ''
|
||||||
|
for klass in self.getClasses(include='all'):
|
||||||
|
depr += '<five:deprecatedManageAddDelete class=".%s.%s"/>\n' % \
|
||||||
|
(klass.name, klass.name)
|
||||||
|
repls['deprecated'] = depr
|
||||||
|
self.copyFile('configure.zcml', repls)
|
||||||
|
|
||||||
def generateConfig(self):
|
def generateConfig(self):
|
||||||
repls = self.repls.copy()
|
repls = self.repls.copy()
|
||||||
# Get some lists of classes
|
# Get some lists of classes
|
||||||
|
|
|
@ -45,6 +45,7 @@ class ToolMixin(BaseMixin):
|
||||||
res['title'] = self.translate(appyType.labelId)
|
res['title'] = self.translate(appyType.labelId)
|
||||||
res['context'] = appyType.context
|
res['context'] = appyType.context
|
||||||
res['action'] = appyType.action
|
res['action'] = appyType.action
|
||||||
|
res['stylesMapping'] = appyType.stylesMapping
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def getSiteUrl(self):
|
def getSiteUrl(self):
|
||||||
|
@ -68,7 +69,7 @@ class ToolMixin(BaseMixin):
|
||||||
template = podInfo['template'].content
|
template = podInfo['template'].content
|
||||||
podTitle = podInfo['title']
|
podTitle = podInfo['title']
|
||||||
if podInfo['context']:
|
if podInfo['context']:
|
||||||
if type(podInfo['context']) == types.FunctionType:
|
if callable(podInfo['context']):
|
||||||
specificPodContext = podInfo['context'](appyObj)
|
specificPodContext = podInfo['context'](appyObj)
|
||||||
else:
|
else:
|
||||||
specificPodContext = podInfo['context']
|
specificPodContext = podInfo['context']
|
||||||
|
@ -76,16 +77,38 @@ class ToolMixin(BaseMixin):
|
||||||
# Temporary file where to generate the result
|
# Temporary file where to generate the result
|
||||||
tempFileName = '%s/%s_%f.%s' % (
|
tempFileName = '%s/%s_%f.%s' % (
|
||||||
getOsTempFolder(), obj.UID(), time.time(), format)
|
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()
|
currentUser = self.portal_membership.getAuthenticatedMember()
|
||||||
podContext = {'tool': appyTool, 'user': currentUser, 'self': appyObj,
|
podContext = {'tool': appyTool, 'user': currentUser, 'self': appyObj,
|
||||||
'now': self.getProductConfig().DateTime(),
|
'now': self.getProductConfig().DateTime(),
|
||||||
'projectFolder': appyTool.getDiskFolder(),
|
'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:
|
if specificPodContext:
|
||||||
podContext.update(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),
|
rendererParams = {'template': StringIO.StringIO(template),
|
||||||
'context': podContext, 'result': tempFileName}
|
'context': podContext, 'result': tempFileName}
|
||||||
|
if stylesMapping:
|
||||||
|
rendererParams['stylesMapping'] = stylesMapping
|
||||||
if appyTool.unoEnabledPython:
|
if appyTool.unoEnabledPython:
|
||||||
rendererParams['pythonWithUnoPath'] = appyTool.unoEnabledPython
|
rendererParams['pythonWithUnoPath'] = appyTool.unoEnabledPython
|
||||||
if appyTool.openOfficePort:
|
if appyTool.openOfficePort:
|
||||||
|
@ -105,7 +128,13 @@ class ToolMixin(BaseMixin):
|
||||||
f = file(tempFileName, 'rb')
|
f = file(tempFileName, 'rb')
|
||||||
res = f.read()
|
res = f.read()
|
||||||
# Identify the filename to return
|
# 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)
|
fileName = appyTool.normalize(fileName)
|
||||||
response = obj.REQUEST.RESPONSE
|
response = obj.REQUEST.RESPONSE
|
||||||
response.setHeader('Content-Type', mimeTypes[format])
|
response.setHeader('Content-Type', mimeTypes[format])
|
||||||
|
@ -189,6 +218,19 @@ class ToolMixin(BaseMixin):
|
||||||
return {'fields': fields, 'nbOfColumns': nbOfColumns,
|
return {'fields': fields, 'nbOfColumns': nbOfColumns,
|
||||||
'fieldDicts': fieldDicts}
|
'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):
|
def getImportElements(self, contentType):
|
||||||
'''Returns the list of elements that can be imported from p_path for
|
'''Returns the list of elements that can be imported from p_path for
|
||||||
p_contentType.'''
|
p_contentType.'''
|
||||||
|
@ -863,4 +905,10 @@ class ToolMixin(BaseMixin):
|
||||||
os.remove(fileName)
|
os.remove(fileName)
|
||||||
return content
|
return content
|
||||||
return 'File does not exist'
|
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')]
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
- mixins/ToolMixin is mixed in with the generated application Tool class.'''
|
- 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
|
import appy.gen
|
||||||
from appy.gen import Type, String, Selection, Role
|
from appy.gen import Type, String, Selection, Role
|
||||||
from appy.gen.utils import *
|
from appy.gen.utils import *
|
||||||
|
@ -67,13 +67,15 @@ class BaseMixin:
|
||||||
initiator.appy().link(fieldName, obj)
|
initiator.appy().link(fieldName, obj)
|
||||||
|
|
||||||
# Call the custom "onEdit" if available
|
# Call the custom "onEdit" if available
|
||||||
|
msg = None # The message to display to the user. It can be set by onEdit
|
||||||
if obj.wrapperClass:
|
if obj.wrapperClass:
|
||||||
appyObject = obj.appy()
|
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
|
# Manage "add" permissions and reindex the object
|
||||||
obj._appy_managePermissions()
|
obj._appy_managePermissions()
|
||||||
obj.reindexObject()
|
obj.reindexObject()
|
||||||
return obj
|
return obj, msg
|
||||||
|
|
||||||
def delete(self):
|
def delete(self):
|
||||||
'''This methods is self's suicide.'''
|
'''This methods is self's suicide.'''
|
||||||
|
@ -219,10 +221,21 @@ class BaseMixin:
|
||||||
return self.skyn.edit(self)
|
return self.skyn.edit(self)
|
||||||
|
|
||||||
# Create or update the object in the database
|
# 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
|
# 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:
|
if rq.get('buttonOk.x', None) or saveConfirmed:
|
||||||
# Go to the consult view for this object
|
# Go to the consult view for this object
|
||||||
obj.plone_utils.addPortalMessage(msg)
|
obj.plone_utils.addPortalMessage(msg)
|
||||||
|
@ -321,8 +334,10 @@ class BaseMixin:
|
||||||
if previousData:
|
if previousData:
|
||||||
self.addDataChange(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.'''
|
'''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)
|
return self.REQUEST.RESPONSE.redirect(url)
|
||||||
|
|
||||||
def showField(self, name, layoutType='view'):
|
def showField(self, name, layoutType='view'):
|
||||||
|
|
13
gen/plone25/skin/callMacro.pt
Normal file
13
gen/plone25/skin/callMacro.pt
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
<tal:comment replace="nothing">
|
||||||
|
This page allows to call any macro from Python code, for example.
|
||||||
|
</tal:comment>
|
||||||
|
<tal:call tal:define="macroName options/macroName;
|
||||||
|
page python: options['page'];
|
||||||
|
contextObj python: options['contextObj'];
|
||||||
|
tool python: contextObj.getTool();
|
||||||
|
layoutType python:'view';
|
||||||
|
putils python: contextObj.plone_utils;
|
||||||
|
portal python: contextObj.portal_url.getPortalObject();
|
||||||
|
portal_url python: contextObj.portal_url();">
|
||||||
|
<metal:callMacro use-macro="python: page.macros[macroName]"/>
|
||||||
|
</tal:call>
|
|
@ -141,7 +141,7 @@
|
||||||
params['filterValue'] = filterWidget.value;
|
params['filterValue'] = filterWidget.value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
askAjaxChunk(hookId,'GET',objectUrl,'macros','queryResult',params);
|
askAjaxChunk(hookId,'GET',objectUrl, 'result', 'queryResult', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
function askObjectHistory(hookId, objectUrl, startNumber) {
|
function askObjectHistory(hookId, objectUrl, startNumber) {
|
||||||
|
@ -244,12 +244,13 @@
|
||||||
createCookie(cookieId, newState);
|
createCookie(cookieId, newState);
|
||||||
}
|
}
|
||||||
// Function that allows to generate a document from a pod template.
|
// Function that allows to generate a document from a pod template.
|
||||||
function generatePodDocument(contextUid, fieldName, podFormat) {
|
function generatePodDocument(contextUid, fieldName, podFormat, queryData) {
|
||||||
var theForm = document.getElementsByName("podTemplateForm")[0];
|
var theForm = document.getElementsByName("podTemplateForm")[0];
|
||||||
theForm.objectUid.value = contextUid;
|
theForm.objectUid.value = contextUid;
|
||||||
theForm.fieldName.value = fieldName;
|
theForm.fieldName.value = fieldName;
|
||||||
theForm.podFormat.value = podFormat;
|
theForm.podFormat.value = podFormat;
|
||||||
theForm.askAction.value = "False";
|
theForm.askAction.value = "False";
|
||||||
|
theForm.queryData.value = queryData;
|
||||||
var askActionWidget = document.getElementById(contextUid + '_' + fieldName);
|
var askActionWidget = document.getElementById(contextUid + '_' + fieldName);
|
||||||
if (askActionWidget && askActionWidget.checked) {
|
if (askActionWidget && askActionWidget.checked) {
|
||||||
theForm.askAction.value = "True";
|
theForm.askAction.value = "True";
|
||||||
|
@ -376,6 +377,7 @@
|
||||||
<input type="hidden" name="fieldName"/>
|
<input type="hidden" name="fieldName"/>
|
||||||
<input type="hidden" name="podFormat"/>
|
<input type="hidden" name="podFormat"/>
|
||||||
<input type="hidden" name="askAction"/>
|
<input type="hidden" name="askAction"/>
|
||||||
|
<input type="hidden" name="queryData"/>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -536,6 +538,7 @@
|
||||||
</tal:comment>
|
</tal:comment>
|
||||||
<div metal:define-macro="header"
|
<div metal:define-macro="header"
|
||||||
tal:define="showCommonInfo python: layoutType == 'view';
|
tal:define="showCommonInfo python: layoutType == 'view';
|
||||||
|
showWorkflow python: tool.getAttr('showWorkflowFor' + contextObj.meta_type);
|
||||||
hasHistory contextObj/hasHistory;
|
hasHistory contextObj/hasHistory;
|
||||||
historyExpanded python: tool.getCookieValue('appyHistory', default='collapsed') == 'expanded';
|
historyExpanded python: tool.getCookieValue('appyHistory', default='collapsed') == 'expanded';
|
||||||
creator contextObj/Creator"
|
creator contextObj/Creator"
|
||||||
|
@ -717,7 +720,7 @@
|
||||||
<metal:message define-macro="message" i18n:domain="plone" >
|
<metal:message define-macro="message" i18n:domain="plone" >
|
||||||
<tal:comment replace="nothing">Single message from portal_status_message request key</tal:comment>
|
<tal:comment replace="nothing">Single message from portal_status_message request key</tal:comment>
|
||||||
<div tal:define="msg request/portal_status_message | nothing"
|
<div tal:define="msg request/portal_status_message | nothing"
|
||||||
tal:condition="msg" class="portalMessage" tal:content="msg" i18n:translate=""></div>
|
tal:condition="msg" class="portalMessage" tal:content="structure msg" i18n:translate=""></div>
|
||||||
|
|
||||||
<tal:comment replace="nothing">Messages added via plone_utils</tal:comment>
|
<tal:comment replace="nothing">Messages added via plone_utils</tal:comment>
|
||||||
<tal:messages define="messages putils/showPortalMessages" condition="messages">
|
<tal:messages define="messages putils/showPortalMessages" condition="messages">
|
||||||
|
|
|
@ -34,9 +34,20 @@
|
||||||
</tal:newSearch>
|
</tal:newSearch>
|
||||||
</legend>
|
</legend>
|
||||||
|
|
||||||
|
<tal:comment replace="nothing">Display here POD templates if required.</tal:comment>
|
||||||
|
<table align="right" cellpadding="0" cellspacing="0"
|
||||||
|
tal:define="widgets python: tool.getResultPodFields(contentType);
|
||||||
|
layoutType python:'view'"
|
||||||
|
tal:condition="python: objs and widgets">
|
||||||
|
<tr><td tal:define="contextObj python: objs[0]"
|
||||||
|
tal:repeat="widget widgets">
|
||||||
|
<metal:pod use-macro="here/skyn/widgets/show/macros/field"/>
|
||||||
|
</td></tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
<table cellpadding="0" cellspacing="0" width="100%"><tr>
|
<table cellpadding="0" cellspacing="0" width="100%"><tr>
|
||||||
<td tal:define="descr python: tool.translate(searchDescr)"
|
<td tal:define="descr python: tool.translate(searchDescr)"
|
||||||
tal:condition="python: searchName and descr">
|
tal:condition="python: searchName and descr.strip()">
|
||||||
<span class="discreet" tal:content="descr"></span><br/><br/>
|
<span class="discreet" tal:content="descr"></span><br/><br/>
|
||||||
</td>
|
</td>
|
||||||
<td align="right" width="25%">
|
<td align="right" width="25%">
|
|
@ -25,8 +25,7 @@
|
||||||
appName appFolder/getId;
|
appName appFolder/getId;
|
||||||
phaseInfo python: contextObj.getAppyPhases(currentOnly=True, layoutType='view');
|
phaseInfo python: contextObj.getAppyPhases(currentOnly=True, layoutType='view');
|
||||||
page request/page|python:'main';
|
page request/page|python:'main';
|
||||||
phase phaseInfo/name;
|
phase phaseInfo/name;">
|
||||||
showWorkflow python: tool.getAttr('showWorkflowFor' + contextObj.meta_type)">
|
|
||||||
<metal:prologue use-macro="here/skyn/page/macros/prologue"/>
|
<metal:prologue use-macro="here/skyn/page/macros/prologue"/>
|
||||||
<metal:show use-macro="here/skyn/page/macros/show"/>
|
<metal:show use-macro="here/skyn/page/macros/show"/>
|
||||||
<metal:footer use-macro="here/skyn/page/macros/footer"/>
|
<metal:footer use-macro="here/skyn/page/macros/footer"/>
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
</tal:askAction>
|
</tal:askAction>
|
||||||
<img tal:repeat="podFormat python: tool.getPodInfo(contextObj, name)['formats']"
|
<img tal:repeat="podFormat python: tool.getPodInfo(contextObj, name)['formats']"
|
||||||
tal:attributes="src string: $portal_url/skyn/${podFormat}.png;
|
tal:attributes="src string: $portal_url/skyn/${podFormat}.png;
|
||||||
onClick python: 'generatePodDocument(\'%s\',\'%s\',\'%s\')' % (contextObj.UID(), name, podFormat);
|
onClick python: 'generatePodDocument(\'%s\',\'%s\',\'%s\',\'%s\')' % (contextObj.UID(), name, podFormat, tool.getQueryInfo());
|
||||||
title podFormat/capitalize"
|
title podFormat/capitalize"
|
||||||
style="cursor:pointer"/>
|
style="cursor:pointer"/>
|
||||||
</metal:view>
|
</metal:view>
|
||||||
|
|
|
@ -239,8 +239,9 @@
|
||||||
refUids python: [o.UID() for o in contextObj.getAppyRefs(name)['objects']];
|
refUids python: [o.UID() for o in contextObj.getAppyRefs(name)['objects']];
|
||||||
isBeingCreated python: contextObj.isTemporary() or ('/portal_factory/' in contextObj.absolute_url())">
|
isBeingCreated python: contextObj.isTemporary() or ('/portal_factory/' in contextObj.absolute_url())">
|
||||||
|
|
||||||
<select tal:attributes="name rname; size widget/height;
|
<select tal:attributes="name rname;
|
||||||
multiple python: isMultiple and 'multiple' or ''">
|
size python: test(isMultiple, widget['height'], '');
|
||||||
|
multiple python: test(isMultiple, 'multiple', '')">
|
||||||
<option tal:condition="not: isMultiple" i18n:translate="choose_a_value"></option>
|
<option tal:condition="not: isMultiple" i18n:translate="choose_a_value"></option>
|
||||||
<tal:ref repeat="refObj allObjects">
|
<tal:ref repeat="refObj allObjects">
|
||||||
<option tal:define="uid python: contextObj.getReferenceUid(refObj)"
|
<option tal:define="uid python: contextObj.getReferenceUid(refObj)"
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
layout The layout object that will dictate how object content
|
layout The layout object that will dictate how object content
|
||||||
will be rendered.
|
will be rendered.
|
||||||
Options:
|
Options:
|
||||||
contextMacro The base on folder containing the macros to call for
|
contextMacro The base folder containing the macros to call for
|
||||||
rendering the elements within the layout.
|
rendering the elements within the layout.
|
||||||
Defaults to portal.skyn
|
Defaults to portal.skyn
|
||||||
</tal:comment>
|
</tal:comment>
|
||||||
|
|
|
@ -4,8 +4,7 @@
|
||||||
xmlns:genericsetup="http://namespaces.zope.org/genericsetup"
|
xmlns:genericsetup="http://namespaces.zope.org/genericsetup"
|
||||||
i18n_domain="<!applicationName!>">
|
i18n_domain="<!applicationName!>">
|
||||||
|
|
||||||
<!--five:deprecatedManageAddDelete class=".Meeting.Meeting"/-->
|
<!deprecated!>
|
||||||
|
|
||||||
<genericsetup:registerProfile name="default"
|
<genericsetup:registerProfile name="default"
|
||||||
title="<!applicationName!>" description=""
|
title="<!applicationName!>" description=""
|
||||||
provides="Products.GenericSetup.interfaces.EXTENSION"
|
provides="Products.GenericSetup.interfaces.EXTENSION"
|
||||||
|
|
|
@ -30,6 +30,11 @@ class ToolWrapper(AbstractWrapper):
|
||||||
return NOT_UNO_ENABLED_PYTHON % value
|
return NOT_UNO_ENABLED_PYTHON % value
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
podOutputFormats = ('odt', 'pdf', 'doc', 'rtf')
|
||||||
|
def getPodOutputFormats(self):
|
||||||
|
'''Gets the available output formats for POD documents.'''
|
||||||
|
return [(of, self.translate(of)) for of in self.podOutputFormats]
|
||||||
|
|
||||||
def getInitiator(self):
|
def getInitiator(self):
|
||||||
'''Retrieves the object that triggered the creation of the object
|
'''Retrieves the object that triggered the creation of the object
|
||||||
being currently created (if any).'''
|
being currently created (if any).'''
|
||||||
|
|
|
@ -16,8 +16,8 @@ WRONG_FILE_TUPLE = 'This is not the way to set a file. You can specify a ' \
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
class AbstractWrapper:
|
class AbstractWrapper:
|
||||||
'''Any real web framework object has a companion object that is an instance
|
'''Any real Zope object has a companion object that is an instance of this
|
||||||
of this class.'''
|
class.'''
|
||||||
def __init__(self, o): self.__dict__['o'] = o
|
def __init__(self, o): self.__dict__['o'] = o
|
||||||
|
|
||||||
def __setattr__(self, name, value):
|
def __setattr__(self, name, value):
|
||||||
|
@ -118,6 +118,7 @@ class AbstractWrapper:
|
||||||
# Update the Archetypes reference field.
|
# Update the Archetypes reference field.
|
||||||
exec 'objs = self.o.g%s()' % postfix
|
exec 'objs = self.o.g%s()' % postfix
|
||||||
if not objs: return
|
if not objs: return
|
||||||
|
if type(objs) not in sequenceTypes: objs = [objs]
|
||||||
# Remove p_obj from existing objects
|
# Remove p_obj from existing objects
|
||||||
if type(obj) in sequenceTypes:
|
if type(obj) in sequenceTypes:
|
||||||
for o in obj:
|
for o in obj:
|
||||||
|
@ -190,15 +191,7 @@ class AbstractWrapper:
|
||||||
appyObj = ploneObj.appy()
|
appyObj = ploneObj.appy()
|
||||||
# Set object attributes
|
# Set object attributes
|
||||||
for attrName, attrValue in kwargs.iteritems():
|
for attrName, attrValue in kwargs.iteritems():
|
||||||
if isinstance(attrValue, AbstractWrapper):
|
setattr(appyObj, attrName, attrValue)
|
||||||
try:
|
|
||||||
refAppyType = getattr(appyObj.__class__.__bases__[-1],
|
|
||||||
attrName)
|
|
||||||
appyObj.link(attrName, attrValue.o)
|
|
||||||
except AttributeError, ae:
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
setattr(appyObj, attrName, attrValue)
|
|
||||||
if isField:
|
if isField:
|
||||||
# Link the object to this one
|
# Link the object to this one
|
||||||
self.link(fieldName, ploneObj)
|
self.link(fieldName, ploneObj)
|
||||||
|
@ -272,7 +265,6 @@ class AbstractWrapper:
|
||||||
elif mType == 'error': mType = 'stop'
|
elif mType == 'error': mType = 'stop'
|
||||||
self.o.plone_utils.addPortalMessage(message, type=mType)
|
self.o.plone_utils.addPortalMessage(message, type=mType)
|
||||||
|
|
||||||
unwantedChars = ('\\', '/', ':', '*', '?', '"', '<', '>', '|', ' ')
|
|
||||||
def normalize(self, s, usage='fileName'):
|
def normalize(self, s, usage='fileName'):
|
||||||
'''Returns a version of string p_s whose special chars have been
|
'''Returns a version of string p_s whose special chars have been
|
||||||
replaced with normal chars.'''
|
replaced with normal chars.'''
|
||||||
|
|
|
@ -109,6 +109,10 @@ class PoMessage:
|
||||||
FILE_REQUIRED = 'Please select a file.'
|
FILE_REQUIRED = 'Please select a file.'
|
||||||
FIELD_INVALID = 'Please fill or correct this.'
|
FIELD_INVALID = 'Please fill or correct this.'
|
||||||
IMAGE_REQUIRED = 'The uploaded file must be an image.'
|
IMAGE_REQUIRED = 'The uploaded file must be an image.'
|
||||||
|
FORMAT_ODT = 'ODT'
|
||||||
|
FORMAT_PDF = 'PDF'
|
||||||
|
FORMAT_DOC = 'DOC'
|
||||||
|
FORMAT_RTF = 'RTF'
|
||||||
|
|
||||||
def __init__(self, id, msg, default, fuzzy=False, comments=[],
|
def __init__(self, id, msg, default, fuzzy=False, comments=[],
|
||||||
niceDefault=False):
|
niceDefault=False):
|
||||||
|
|
|
@ -84,8 +84,10 @@ class BufferIterator:
|
||||||
self.remainingElemIndexes = self.buffer.elements.keys()
|
self.remainingElemIndexes = self.buffer.elements.keys()
|
||||||
self.remainingSubBufferIndexes.sort()
|
self.remainingSubBufferIndexes.sort()
|
||||||
self.remainingElemIndexes.sort()
|
self.remainingElemIndexes.sort()
|
||||||
|
|
||||||
def hasNext(self):
|
def hasNext(self):
|
||||||
return self.remainingSubBufferIndexes or self.remainingElemIndexes
|
return self.remainingSubBufferIndexes or self.remainingElemIndexes
|
||||||
|
|
||||||
def next(self):
|
def next(self):
|
||||||
nextSubBufferIndex = None
|
nextSubBufferIndex = None
|
||||||
if self.remainingSubBufferIndexes:
|
if self.remainingSubBufferIndexes:
|
||||||
|
@ -113,36 +115,45 @@ class BufferIterator:
|
||||||
class Buffer:
|
class Buffer:
|
||||||
'''Abstract class representing any buffer used during rendering.'''
|
'''Abstract class representing any buffer used during rendering.'''
|
||||||
elementRex = re.compile('([\w-]+:[\w-]+)\s*(.*?)>', re.S)
|
elementRex = re.compile('([\w-]+:[\w-]+)\s*(.*?)>', re.S)
|
||||||
|
|
||||||
def __init__(self, env, parent):
|
def __init__(self, env, parent):
|
||||||
self.parent = parent
|
self.parent = parent
|
||||||
self.subBuffers = {} # ~{i_bufferIndex: Buffer}~
|
self.subBuffers = {} # ~{i_bufferIndex: Buffer}~
|
||||||
self.env = env
|
self.env = env
|
||||||
|
|
||||||
def addSubBuffer(self, subBuffer=None):
|
def addSubBuffer(self, subBuffer=None):
|
||||||
if not subBuffer:
|
if not subBuffer:
|
||||||
subBuffer = MemoryBuffer(self.env, self)
|
subBuffer = MemoryBuffer(self.env, self)
|
||||||
self.subBuffers[self.getLength()] = subBuffer
|
self.subBuffers[self.getLength()] = subBuffer
|
||||||
subBuffer.parent = self
|
subBuffer.parent = self
|
||||||
return subBuffer
|
return subBuffer
|
||||||
|
|
||||||
def removeLastSubBuffer(self):
|
def removeLastSubBuffer(self):
|
||||||
subBufferIndexes = self.subBuffers.keys()
|
subBufferIndexes = self.subBuffers.keys()
|
||||||
subBufferIndexes.sort()
|
subBufferIndexes.sort()
|
||||||
lastIndex = subBufferIndexes.pop()
|
lastIndex = subBufferIndexes.pop()
|
||||||
del self.subBuffers[lastIndex]
|
del self.subBuffers[lastIndex]
|
||||||
|
|
||||||
def write(self, something): pass # To be overridden
|
def write(self, something): pass # To be overridden
|
||||||
|
|
||||||
def getLength(self): pass # To be overridden
|
def getLength(self): pass # To be overridden
|
||||||
|
|
||||||
def dumpStartElement(self, elem, attrs={}):
|
def dumpStartElement(self, elem, attrs={}):
|
||||||
self.write('<%s' % elem)
|
self.write('<%s' % elem)
|
||||||
for name, value in attrs.items():
|
for name, value in attrs.items():
|
||||||
self.write(' %s="%s"' % (name, value))
|
self.write(' %s="%s"' % (name, value))
|
||||||
self.write('>')
|
self.write('>')
|
||||||
|
|
||||||
def dumpEndElement(self, elem):
|
def dumpEndElement(self, elem):
|
||||||
self.write('</%s>' % elem)
|
self.write('</%s>' % elem)
|
||||||
|
|
||||||
def dumpElement(self, elem, content=None, attrs={}):
|
def dumpElement(self, elem, content=None, attrs={}):
|
||||||
'''For dumping a whole element at once.'''
|
'''For dumping a whole element at once.'''
|
||||||
self.dumpStartElement(elem, attrs)
|
self.dumpStartElement(elem, attrs)
|
||||||
if content:
|
if content:
|
||||||
self.dumpContent(content)
|
self.dumpContent(content)
|
||||||
self.dumpEndElement(elem)
|
self.dumpEndElement(elem)
|
||||||
|
|
||||||
def dumpContent(self, content):
|
def dumpContent(self, content):
|
||||||
'''Dumps string p_content into the buffer.'''
|
'''Dumps string p_content into the buffer.'''
|
||||||
for c in content:
|
for c in content:
|
||||||
|
@ -150,6 +161,7 @@ class Buffer:
|
||||||
self.write(XML_SPECIAL_CHARS[c])
|
self.write(XML_SPECIAL_CHARS[c])
|
||||||
else:
|
else:
|
||||||
self.write(c)
|
self.write(c)
|
||||||
|
|
||||||
def dumpAttribute(self, name, value):
|
def dumpAttribute(self, name, value):
|
||||||
self.write(''' %s="%s" ''' % (name, value))
|
self.write(''' %s="%s" ''' % (name, value))
|
||||||
|
|
||||||
|
@ -160,17 +172,21 @@ class FileBuffer(Buffer):
|
||||||
self.result = result
|
self.result = result
|
||||||
self.content = file(result, 'w')
|
self.content = file(result, 'w')
|
||||||
self.content.write(xmlPrologue)
|
self.content.write(xmlPrologue)
|
||||||
def getLength(self): return 0
|
|
||||||
# getLength is used to manage insertions into sub-buffers. But in the case
|
# getLength is used to manage insertions into sub-buffers. But in the case
|
||||||
# of a FileBuffer, we will only have 1 sub-buffer at a time, and we don't
|
# of a FileBuffer, we will only have 1 sub-buffer at a time, and we don't
|
||||||
# care about where it will be inserted into the FileBuffer.
|
# care about where it will be inserted into the FileBuffer.
|
||||||
|
def getLength(self): return 0
|
||||||
|
|
||||||
def write(self, something):
|
def write(self, something):
|
||||||
self.content.write(something.encode('utf-8'))
|
self.content.write(something.encode('utf-8'))
|
||||||
|
|
||||||
def addExpression(self, expression):
|
def addExpression(self, expression):
|
||||||
try:
|
try:
|
||||||
self.dumpContent(Expression(expression).evaluate(self.env.context))
|
self.dumpContent(Expression(expression).evaluate(self.env.context))
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
PodError.dump(self, EVAL_EXPR_ERROR % (expression, e), dumpTb=False)
|
PodError.dump(self, EVAL_EXPR_ERROR % (expression, e), dumpTb=False)
|
||||||
|
|
||||||
def pushSubBuffer(self, subBuffer): pass
|
def pushSubBuffer(self, subBuffer): pass
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
@ -179,24 +195,30 @@ class MemoryBuffer(Buffer):
|
||||||
'(?:\s+(for|if|else|with)\s*(.*))?')
|
'(?:\s+(for|if|else|with)\s*(.*))?')
|
||||||
forRex = re.compile('\s*([\w\-_]+)\s+in\s+(.*)')
|
forRex = re.compile('\s*([\w\-_]+)\s+in\s+(.*)')
|
||||||
varRex = re.compile('\s*([\w\-_]+)\s*=\s*(.*)')
|
varRex = re.compile('\s*([\w\-_]+)\s*=\s*(.*)')
|
||||||
|
|
||||||
def __init__(self, env, parent):
|
def __init__(self, env, parent):
|
||||||
Buffer.__init__(self, env, parent)
|
Buffer.__init__(self, env, parent)
|
||||||
self.content = u''
|
self.content = u''
|
||||||
self.elements = {}
|
self.elements = {}
|
||||||
self.action = None
|
self.action = None
|
||||||
|
|
||||||
def addSubBuffer(self, subBuffer=None):
|
def addSubBuffer(self, subBuffer=None):
|
||||||
sb = Buffer.addSubBuffer(self, subBuffer)
|
sb = Buffer.addSubBuffer(self, subBuffer)
|
||||||
self.content += ' ' # To avoid having several subbuffers referenced at
|
self.content += ' ' # To avoid having several subbuffers referenced at
|
||||||
# the same place within this buffer.
|
# the same place within this buffer.
|
||||||
return sb
|
return sb
|
||||||
|
|
||||||
def getFileBuffer(self):
|
def getFileBuffer(self):
|
||||||
if isinstance(self.parent, FileBuffer):
|
if isinstance(self.parent, FileBuffer):
|
||||||
res = self.parent
|
res = self.parent
|
||||||
else:
|
else:
|
||||||
res = self.parent.getFileBuffer()
|
res = self.parent.getFileBuffer()
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def getLength(self): return len(self.content)
|
def getLength(self): return len(self.content)
|
||||||
|
|
||||||
def write(self, thing): self.content += thing
|
def write(self, thing): self.content += thing
|
||||||
|
|
||||||
def getIndex(self, podElemName):
|
def getIndex(self, podElemName):
|
||||||
res = -1
|
res = -1
|
||||||
for index, podElem in self.elements.iteritems():
|
for index, podElem in self.elements.iteritems():
|
||||||
|
@ -204,11 +226,13 @@ class MemoryBuffer(Buffer):
|
||||||
if index > res:
|
if index > res:
|
||||||
res = index
|
res = index
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def getMainElement(self):
|
def getMainElement(self):
|
||||||
res = None
|
res = None
|
||||||
if self.elements.has_key(0):
|
if self.elements.has_key(0):
|
||||||
res = self.elements[0]
|
res = self.elements[0]
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def isMainElement(self, elem):
|
def isMainElement(self, elem):
|
||||||
res = False
|
res = False
|
||||||
mainElem = self.getMainElement()
|
mainElem = self.getMainElement()
|
||||||
|
@ -221,6 +245,7 @@ class MemoryBuffer(Buffer):
|
||||||
res = False
|
res = False
|
||||||
break
|
break
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def unreferenceElement(self, elem):
|
def unreferenceElement(self, elem):
|
||||||
# Find last occurrence of this element
|
# Find last occurrence of this element
|
||||||
elemIndex = -1
|
elemIndex = -1
|
||||||
|
@ -229,6 +254,7 @@ class MemoryBuffer(Buffer):
|
||||||
if (podElem.OD.elem == elem) and (index > elemIndex):
|
if (podElem.OD.elem == elem) and (index > elemIndex):
|
||||||
elemIndex = index
|
elemIndex = index
|
||||||
del self.elements[elemIndex]
|
del self.elements[elemIndex]
|
||||||
|
|
||||||
def pushSubBuffer(self, subBuffer):
|
def pushSubBuffer(self, subBuffer):
|
||||||
'''Sets p_subBuffer at the very end of the buffer.'''
|
'''Sets p_subBuffer at the very end of the buffer.'''
|
||||||
subIndex = None
|
subIndex = None
|
||||||
|
@ -242,6 +268,7 @@ class MemoryBuffer(Buffer):
|
||||||
del self.subBuffers[subIndex]
|
del self.subBuffers[subIndex]
|
||||||
self.subBuffers[self.getLength()] = subBuffer
|
self.subBuffers[self.getLength()] = subBuffer
|
||||||
self.content += u' '
|
self.content += u' '
|
||||||
|
|
||||||
def transferAllContent(self):
|
def transferAllContent(self):
|
||||||
'''Transfer all content to parent.'''
|
'''Transfer all content to parent.'''
|
||||||
if isinstance(self.parent, FileBuffer):
|
if isinstance(self.parent, FileBuffer):
|
||||||
|
@ -263,18 +290,21 @@ class MemoryBuffer(Buffer):
|
||||||
MemoryBuffer.__init__(self, self.env, self.parent)
|
MemoryBuffer.__init__(self, self.env, self.parent)
|
||||||
# Change buffer position wrt parent
|
# Change buffer position wrt parent
|
||||||
self.parent.pushSubBuffer(self)
|
self.parent.pushSubBuffer(self)
|
||||||
|
|
||||||
def addElement(self, elem):
|
def addElement(self, elem):
|
||||||
newElem = PodElement.create(elem)
|
newElem = PodElement.create(elem)
|
||||||
self.elements[self.getLength()] = newElem
|
self.elements[self.getLength()] = newElem
|
||||||
if isinstance(newElem, Cell) or isinstance(newElem, Table):
|
if isinstance(newElem, Cell) or isinstance(newElem, Table):
|
||||||
newElem.tableInfo = self.env.getTable()
|
newElem.tableInfo = self.env.getTable()
|
||||||
|
|
||||||
def addExpression(self, expression):
|
def addExpression(self, expression):
|
||||||
# Create the POD expression
|
# Create the POD expression
|
||||||
expr = Expression(expression)
|
expr = Expression(expression)
|
||||||
expr.expr = expression
|
expr.expr = expression
|
||||||
self.elements[self.getLength()] = expr
|
self.elements[self.getLength()] = expr
|
||||||
self.content += u' ' # To be sure that an expr and an elem can't be found
|
self.content += u' '# To be sure that an expr and an elem can't be found
|
||||||
# at the same index in the buffer.
|
# at the same index in the buffer.
|
||||||
|
|
||||||
def createAction(self, statementGroup):
|
def createAction(self, statementGroup):
|
||||||
'''Tries to create an action based on p_statementGroup. If the statement
|
'''Tries to create an action based on p_statementGroup. If the statement
|
||||||
is not correct, r_ is -1. Else, r_ is the index of the element within
|
is not correct, r_ is -1. Else, r_ is the index of the element within
|
||||||
|
@ -363,6 +393,7 @@ class MemoryBuffer(Buffer):
|
||||||
except ParsingError, ppe:
|
except ParsingError, ppe:
|
||||||
PodError.dump(self, ppe, removeFirstLine=True)
|
PodError.dump(self, ppe, removeFirstLine=True)
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def cut(self, index, keepFirstPart):
|
def cut(self, index, keepFirstPart):
|
||||||
'''Cuts this buffer into 2 parts. Depending on p_keepFirstPart, the 1st
|
'''Cuts this buffer into 2 parts. Depending on p_keepFirstPart, the 1st
|
||||||
(from 0 to index-1) or the second (from index to the end) part of the
|
(from 0 to index-1) or the second (from index to the end) part of the
|
||||||
|
@ -418,6 +449,7 @@ class MemoryBuffer(Buffer):
|
||||||
res.write(self.content[:index])
|
res.write(self.content[:index])
|
||||||
self.content = self.content[index:]
|
self.content = self.content[index:]
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def getElementIndexes(self, expressions=True):
|
def getElementIndexes(self, expressions=True):
|
||||||
res = []
|
res = []
|
||||||
for index, elem in self.elements.iteritems():
|
for index, elem in self.elements.iteritems():
|
||||||
|
@ -427,6 +459,7 @@ class MemoryBuffer(Buffer):
|
||||||
if condition:
|
if condition:
|
||||||
res.append(index)
|
res.append(index)
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def transferActionIndependentContent(self, actionElemIndex):
|
def transferActionIndependentContent(self, actionElemIndex):
|
||||||
# Manage content to transfer to parent buffer
|
# Manage content to transfer to parent buffer
|
||||||
if actionElemIndex != 0:
|
if actionElemIndex != 0:
|
||||||
|
@ -451,6 +484,7 @@ class MemoryBuffer(Buffer):
|
||||||
else:
|
else:
|
||||||
res = self
|
res = self
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def getStartIndex(self, removeMainElems):
|
def getStartIndex(self, removeMainElems):
|
||||||
'''When I must dump the buffer, sometimes (if p_removeMainElems is
|
'''When I must dump the buffer, sometimes (if p_removeMainElems is
|
||||||
True), I must dump only a subset of it. This method returns the start
|
True), I must dump only a subset of it. This method returns the start
|
||||||
|
@ -476,6 +510,7 @@ class MemoryBuffer(Buffer):
|
||||||
else:
|
else:
|
||||||
res = 0
|
res = 0
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def getStopIndex(self, removeMainElems):
|
def getStopIndex(self, removeMainElems):
|
||||||
'''This method returns the stop index of the buffer part I must dump.'''
|
'''This method returns the stop index of the buffer part I must dump.'''
|
||||||
if removeMainElems:
|
if removeMainElems:
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,USA.
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,USA.
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
import os, os.path, sys, traceback, unicodedata, shutil
|
import os, os.path, re, sys, traceback, unicodedata, shutil
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
class FolderDeleter:
|
class FolderDeleter:
|
||||||
|
@ -151,21 +151,35 @@ def executeCommand(cmd):
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
unwantedChars = ('\\', '/', ':', '*', '?', '"', '<', '>', '|', ' ')
|
unwantedChars = ('\\', '/', ':', '*', '?', '"', '<', '>', '|', ' ')
|
||||||
|
alphaRex = re.compile('[a-zA-Z]')
|
||||||
|
alphanumRex = re.compile('[a-zA-Z0-9]')
|
||||||
def normalizeString(s, usage='fileName'):
|
def normalizeString(s, usage='fileName'):
|
||||||
'''Returns a version of string p_s whose special chars have been
|
'''Returns a version of string p_s whose special chars (like accents) have
|
||||||
replaced with normal chars.'''
|
been replaced with normal chars. Moreover, if p_usage is:
|
||||||
|
* fileName: it removes any char that can't be part of a file name;
|
||||||
|
* alphanum: it removes any non-alphanumeric char;
|
||||||
|
* alpha: it removes any non-letter char.
|
||||||
|
'''
|
||||||
# We work in unicode. Convert p_s to unicode if not unicode.
|
# We work in unicode. Convert p_s to unicode if not unicode.
|
||||||
if isinstance(s, str): s = s.decode('utf-8')
|
if isinstance(s, str): s = s.decode('utf-8')
|
||||||
elif not isinstance(s, unicode): s = unicode(s)
|
elif not isinstance(s, unicode): s = unicode(s)
|
||||||
|
# Remove any special char like accents.
|
||||||
|
s = unicodedata.normalize('NFKD', s).encode('ascii', 'ignore')
|
||||||
|
# Remove any other char, depending on p_usage.
|
||||||
if usage == 'fileName':
|
if usage == 'fileName':
|
||||||
# Remove any char that can't be found within a file name under
|
# Remove any char that can't be found within a file name under
|
||||||
# Windows or that could lead to problems with OpenOffice.
|
# Windows or that could lead to problems with OpenOffice.
|
||||||
res = ''
|
res = ''
|
||||||
for char in s:
|
for char in s:
|
||||||
if char not in unwantedChars:
|
if char not in unwantedChars: res += char
|
||||||
res += char
|
elif usage.startswith('alpha'):
|
||||||
s = res
|
exec 'rex = %sRex' % usage
|
||||||
return unicodedata.normalize('NFKD', s).encode("ascii","ignore")
|
res = ''
|
||||||
|
for char in s:
|
||||||
|
if rex.match(char): res += char
|
||||||
|
else:
|
||||||
|
res = s
|
||||||
|
return res
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
typeLetters = {'b': bool, 'i': int, 'j': long, 'f':float, 's':str, 'u':unicode,
|
typeLetters = {'b': bool, 'i': int, 'j': long, 'f':float, 's':str, 'u':unicode,
|
||||||
|
|
Loading…
Reference in a new issue