Gaetan Delannay 2009-07-10 15:01:50 +02:00
parent ec17f900a6
commit 10eea7d735
19 changed files with 1770 additions and 1644 deletions

View file

@ -408,9 +408,11 @@ class Generator(AbstractGenerator):
self.copyFile('workflows.py', repls, destFolder='Extensions')
def generateWrapperProperty(self, attrName, appyType):
# Generate getter
'''Generates the getter for attribute p_attrName having type
p_appyType.'''
res = ' def get_%s(self):\n' % attrName
blanks = ' '*8
getterName = 'get%s%s' % (attrName[0].upper(), attrName[1:])
if isinstance(appyType, Ref):
res += blanks + 'return self.o._appy_getRefs("%s", ' \
'noListIfSingleObj=True)\n' % attrName
@ -418,8 +420,11 @@ class Generator(AbstractGenerator):
res += blanks + 'appyType = getattr(self.klass, "%s")\n' % attrName
res += blanks + 'return self.o.getComputedValue(' \
'appyType.__dict__)\n'
elif isinstance(appyType, File):
res += blanks + 'v = self.o.%s()\n' % getterName
res += blanks + 'if not v: return None\n'
res += blanks + 'else: return FileWrapper(v)\n'
else:
getterName = 'get%s%s' % (attrName[0].upper(), attrName[1:])
if attrName in ArchetypeFieldDescriptor.specialParams:
getterName = attrName.capitalize()
res += blanks + 'return self.o.%s()\n' % getterName

View file

@ -142,9 +142,14 @@ class ToolMixin(AbstractMixin):
else:
# Create a FieldDescr instance
appyType = anObject.getAppyType(fieldName)
atField = anObject.schema.get(fieldName)
fieldDescr = FieldDescr(atField, appyType, None)
res.append(fieldDescr.get())
if not appyType:
res.append({'atField': None, 'name': fieldName})
# The field name is wrong.
# We return it so we can show it in an error message.
else:
atField = anObject.schema.get(fieldName)
fieldDescr = FieldDescr(atField, appyType, None)
res.append(fieldDescr.get())
return res
xhtmlToText = re.compile('<.*?>', re.S)

View file

@ -654,7 +654,8 @@
<tal:comment replace="nothing">Columns corresponding to other fields</tal:comment>
<tal:otherFields repeat="fieldDescr fieldDescrs">
<tal:standardField condition="python: fieldDescr != 'workflowState'">
<td tal:attributes="id python:'field_%s' % fieldDescr['atField'].getName()">
<td tal:condition="fieldDescr/atField"
tal:attributes="id python:'field_%s' % fieldDescr['atField'].getName()">
<tal:field define="contextObj python:obj;
isEdit python:False;
showLabel python:False;
@ -663,6 +664,9 @@
<metal:field use-macro="here/<!macros!>/macros/showArchetypesField"/>
</tal:field>
</td>
<td tal:condition="not: fieldDescr/atField" style="color:red">Field
<span tal:replace="fieldDescr/name"/> not found.
</td>
</tal:standardField>
<tal:workflowState condition="python: fieldDescr == 'workflowState'">
<td id="field_workflow_state" i18n:translate="" tal:content="obj/getWorkflowLabel"></td>

View file

@ -1,6 +1,6 @@
# ------------------------------------------------------------------------------
from appy.gen import *
from appy.gen.plone25.wrappers import AbstractWrapper
from appy.gen.plone25.wrappers import AbstractWrapper, FileWrapper
from appy.gen.plone25.wrappers.ToolWrapper import ToolWrapper
from appy.gen.plone25.wrappers.FlavourWrapper import FlavourWrapper
from appy.gen.plone25.wrappers.PodTemplateWrapper import PodTemplateWrapper

View file

@ -18,6 +18,7 @@ from OFS.Image import File
from DateTime import DateTime
from Products.CMFCore.utils import getToolByName
from Products.CMFPlone.PloneBatch import Batch
from OFS.Image import File
import logging
logger = logging.getLogger('<!applicationName!>')

View file

@ -2,7 +2,14 @@
developer the real classes used by the undelrying web framework.'''
# ------------------------------------------------------------------------------
import time
import time, os.path, mimetypes
from appy.gen.utils import sequenceTypes
from appy.shared.utils import getOsTempFolder
# Some error messages ----------------------------------------------------------
WRONG_FILE_TUPLE = 'This is not the way to set a file. You can specify a ' \
'2-tuple (fileName, fileContent) or a 3-tuple (fileName, fileContent, ' \
'mimeType).'
# ------------------------------------------------------------------------------
class AbstractWrapper:
@ -10,8 +17,56 @@ class AbstractWrapper:
of this class.'''
def __init__(self, o):
self.__dict__['o'] = o
def _set_file_attribute(self, name, v):
'''Updates the value of a file attribute named p_name with value p_v.
p_v may be:
- a string value containing the path to a file on disk;
- a 2-tuple (fileName, fileContent) where
* fileName = the name of the file (ie "myFile.odt")
* fileContent = the binary or textual content of the file or an
open file handler.
- a 3-tuple (fileName, fileContent, mimeType) where mimeType is the
v MIME type of the file.'''
ploneFileClass = self.o.getProductConfig().File
if isinstance(v, ploneFileClass):
exec "self.o.set%s%s(v)" % (name[0].upper(), name[1:])
elif isinstance(v, FileWrapper):
setattr(self, name, v._atFile)
elif isinstance(v, basestring):
f = file(v)
fileName = os.path.basename(v)
fileId = 'file.%f' % time.time()
ploneFile = ploneFileClass(fileId, fileName, f)
ploneFile.filename = fileName
ploneFile.content_type = mimetypes.guess_type(fileName)[0]
setattr(self, name, ploneFile)
f.close()
elif type(v) in sequenceTypes:
# It should be a 2-tuple or 3-tuple
fileName = None
mimeType = None
if len(v) == 2:
fileName, fileContent = v
elif len(v) == 3:
fileName, fileContent, mimeType = v
else:
raise WRONG_FILE_TUPLE
if fileName:
fileId = 'file.%f' % time.time()
ploneFile = ploneFileClass(fileId, fileName, fileContent)
ploneFile.filename = fileName
if not mimeType:
mimeType = mimetypes.guess_type(fileName)[0]
ploneFile.content_type = mimeType
setattr(self, name, ploneFile)
def __setattr__(self, name, v):
exec "self.o.set%s%s(v)" % (name[0].upper(), name[1:])
appyType = self.o.getAppyType(name)
if not appyType and (name != 'title'):
raise 'Attribute "%s" does not exist.' % name
if appyType and (appyType['type'] == 'File'):
self._set_file_attribute(name, v)
else:
exec "self.o.set%s%s(v)" % (name[0].upper(), name[1:])
def __cmp__(self, other):
if other:
return cmp(self.o, other.o)
@ -20,6 +75,9 @@ class AbstractWrapper:
def get_tool(self):
return self.o.getTool()._appy_getWrapper(force=True)
tool = property(get_tool)
def get_flavour(self):
return self.o.getTool().getFlavour(self.o, appy=True)
flavour = property(get_flavour)
def get_session(self):
return self.o.REQUEST.SESSION
session = property(get_session)
@ -61,21 +119,37 @@ class AbstractWrapper:
sortedRefField
getattr(self.o, sortedRefField).append(obj.UID())
def create(self, fieldName, **kwargs):
'''This method allows to create an object and link it to the current
one through reference field named p_fieldName.'''
# Determine object id and portal type
portalType = self.o.getAppyRefPortalType(fieldName)
def create(self, fieldNameOrClass, **kwargs):
'''If p_fieldNameOfClass is the name of a field, this method allows to
create an object and link it to the current one (self) through
reference field named p_fieldName.
If p_fieldNameOrClass is a class from the gen-application, it must
correspond to a root class and this method allows to create a
root object in the application folder.'''
isField = isinstance(fieldNameOrClass, basestring)
# Determine the portal type of the object to create
if isField:
fieldName = fieldNameOrClass
idPrefix = fieldName
portalType = self.o.getAppyRefPortalType(fieldName)
else:
theClass = fieldNameOrClass
idPrefix = theClass.__name__
portalType = self.o._appy_getAtType(theClass, self.flavour.o)
# Determine object id
if kwargs.has_key('id'):
objId = kwargs['id']
del kwargs['id']
else:
objId = '%s.%f' % (fieldName, time.time())
# Where must I create te object?
if hasattr(self, 'folder') and self.folder:
folder = self.o
objId = '%s.%f' % (idPrefix, time.time())
# Where must I create the object?
if not isField:
folder = self.o.getTool().getAppFolder()
else:
folder = self.o.getParentNode()
if hasattr(self, 'folder') and self.folder:
folder = self.o
else:
folder = self.o.getParentNode()
# Create the object
folder.invokeFactory(portalType, objId)
ploneObj = getattr(folder, objId)
@ -93,13 +167,15 @@ class AbstractWrapper:
pass
else:
getattr(ploneObj, setterName)(attrValue)
# Link the object to this one
self.link(fieldName, ploneObj)
if isField:
# Link the object to this one
self.link(fieldName, ploneObj)
self.o.reindexObject()
# Call custom initialization
try:
appyObj.onEdit(True) # Call custom initialization
appyObj.onEdit(True)
except AttributeError:
pass
self.o.reindexObject()
ploneObj.reindexObject()
return appyObj
@ -133,4 +209,49 @@ class AbstractWrapper:
self.o._v_appy_do = {'doAction': doAction, 'doNotify': doNotify}
wfTool.doActionFor(self.o, transitionName, comment=comment)
del self.o._v_appy_do
# ------------------------------------------------------------------------------
class FileWrapper:
'''When you get, from an appy object, the value of a File attribute, you
get an instance of this class.'''
def __init__(self, atFile):
'''This constructor is only used by Appy to create a nice File instance
from a Plone/Zope corresponding instance (p_atFile). If you need to
create a new file and assign it to a File attribute, use the
attribute setter, do not create yourself an instance of this
class.'''
d = self.__dict__
d['_atFile'] = atFile # Not for you!
d['name'] = atFile.filename
d['content'] = atFile.data
d['mimeType'] = atFile.content_type
d['size'] = atFile.size # In bytes
def __setattr__(self, name, v):
d = self.__dict__
if name == 'name':
self._atFile.filename = v
d['name'] = v
elif name == 'content':
self._atFile.update_data(v, self.mimeType, len(v))
d['content'] = v
d['size'] = len(v)
elif name == 'mimeType':
self._atFile.content_type = self.mimeType = v
else:
raise 'Impossible to set attribute %s. "Settable" attributes ' \
'are "name", "content" and "mimeType".' % name
def dump(self, filePath=None):
'''Writes the file on disk. If p_filePath is specified, it is the
path name where the file will be dumped; folders mentioned in it
must exist. If not, the file will be dumped in the OS temp folder.
The absoulte path name of the dumped file is returned.'''
if not filePath:
filePath = '%s/file%f.%s' % (getOsTempFolder(), time.time(),
self.name)
f = file(filePath, 'w')
f.write(self.content)
f.close()
return filePath
# ------------------------------------------------------------------------------