[gen] Display action fields with layout 'buttons' in query results and tied objects for refs, besides transitions. [gen] Show delete button when allowed on the view layout. [gen] Improved method AbstractWrapper::createFrom.
This commit is contained in:
parent
225ea927a4
commit
746a6cd52d
|
@ -30,21 +30,23 @@ class Action(Field):
|
||||||
<form var="formId='%s_%s_form' % (zobj.id, name);
|
<form var="formId='%s_%s_form' % (zobj.id, name);
|
||||||
label=_(field.labelId);
|
label=_(field.labelId);
|
||||||
descr=field.hasDescr and _(field.descrId) or None;
|
descr=field.hasDescr and _(field.descrId) or None;
|
||||||
buttonWidth=ztool.getButtonWidth(label)"
|
buttonWidth=ztool.getButtonWidth(label);
|
||||||
|
smallButtons=smallButtons|False;
|
||||||
|
css=smallButtons and 'buttonSmall button' or 'button'"
|
||||||
id=":formId" action=":ztool.absolute_url() + '/do'"
|
id=":formId" action=":ztool.absolute_url() + '/do'"
|
||||||
style="display:inline">
|
style="display:inline">
|
||||||
<input type="hidden" name="action" value="ExecuteAction"/>
|
<input type="hidden" name="action" value="ExecuteAction"/>
|
||||||
<input type="hidden" name="objectUid" value=":zobj.id"/>
|
<input type="hidden" name="objectUid" value=":zobj.id"/>
|
||||||
<input type="hidden" name="fieldName" value=":name"/>
|
<input type="hidden" name="fieldName" value=":name"/>
|
||||||
<input type="hidden" name="comment" value=""/>
|
<input type="hidden" name="comment" value=""/>
|
||||||
<input if="field.confirm" type="button" class="button" title=":descr"
|
<input if="field.confirm" type="button" class=":css" title=":descr"
|
||||||
var="labelConfirm=_(field.labelId + '_confirm');
|
var="labelConfirm=_(field.labelId + '_confirm');
|
||||||
commentParam=(field.confirm == 'text') and 'true' or 'false'"
|
commentParam=(field.confirm == 'text') and 'true' or 'false'"
|
||||||
value=":label"
|
value=":label"
|
||||||
style=":'%s; %s' % (url(field.icon, bg=True), buttonWidth)"
|
style=":'%s; %s' % (url(field.icon, bg=True), buttonWidth)"
|
||||||
onclick=":'askConfirm(%s,%s,%s,%s)' % (q('form'), q(formId), \
|
onclick=":'askConfirm(%s,%s,%s,%s)' % (q('form'), q(formId), \
|
||||||
q(labelConfirm), commentParam)"/>
|
q(labelConfirm), commentParam)"/>
|
||||||
<input if="not field.confirm" type="submit" class="button" name="do"
|
<input if="not field.confirm" type="submit" class=":css" name="do"
|
||||||
value=":label" title=":descr"
|
value=":label" title=":descr"
|
||||||
style=":'%s; %s' % (url(field.icon, bg=True), buttonWidth)"/>
|
style=":'%s; %s' % (url(field.icon, bg=True), buttonWidth)"/>
|
||||||
</form>''')
|
</form>''')
|
||||||
|
|
|
@ -86,10 +86,10 @@ class FileInfo:
|
||||||
self.uploadName = uploadName # The name of the uploaded file
|
self.uploadName = uploadName # The name of the uploaded file
|
||||||
self.size = 0 # Its size, in bytes
|
self.size = 0 # Its size, in bytes
|
||||||
self.mimeType = None # Its MIME type
|
self.mimeType = None # Its MIME type
|
||||||
self.modified = None # The last modification date for this file.
|
self.modified = None # The last modification date for this file
|
||||||
# Complete metadata if p_inDb is False
|
# Complete metadata if p_inDb is False
|
||||||
if not inDb:
|
if not inDb:
|
||||||
self.fsName = '' # Already included in self.fsPath.
|
self.fsName = '' # Already included in self.fsPath
|
||||||
# We will not store p_inDb. Checking if self.fsName is the empty
|
# We will not store p_inDb. Checking if self.fsName is the empty
|
||||||
# string is equivalent.
|
# string is equivalent.
|
||||||
fileInfo = os.stat(self.fsPath)
|
fileInfo = os.stat(self.fsPath)
|
||||||
|
@ -166,20 +166,25 @@ class FileInfo:
|
||||||
'''Writes to the filesystem the p_fileObj file, that can be:
|
'''Writes to the filesystem the p_fileObj file, that can be:
|
||||||
- a Zope FileUpload (coming from a HTTP post);
|
- a Zope FileUpload (coming from a HTTP post);
|
||||||
- a OFS.Image.File object (legacy within-ZODB file object);
|
- a OFS.Image.File object (legacy within-ZODB file object);
|
||||||
|
- another ("not-in-DB") FileInfo instance;
|
||||||
- a tuple (fileName, fileContent, mimeType)
|
- a tuple (fileName, fileContent, mimeType)
|
||||||
(see doc in method File.store below).'''
|
(see doc in method File.store below).'''
|
||||||
# Determine p_fileObj's type
|
# Determine p_fileObj's type
|
||||||
fileType = fileObj.__class__.__name__
|
fileType = fileObj.__class__.__name__
|
||||||
# Set MIME type
|
# Determine the MIME type and the base name of the file to store
|
||||||
if fileType == 'FileUpload':
|
if fileType == 'FileUpload':
|
||||||
mimeType = self.getMimeTypeFromFileUpload(fileObj)
|
mimeType = self.getMimeTypeFromFileUpload(fileObj)
|
||||||
|
fileName = fileObj.filename
|
||||||
elif fileType == 'File':
|
elif fileType == 'File':
|
||||||
mimeType = fileObj.content_type
|
mimeType = fileObj.content_type
|
||||||
|
fileName = fileObj.filename
|
||||||
|
elif fileType == 'FileInfo':
|
||||||
|
mimeType = fileObj.mimeType
|
||||||
|
fileName = fileObj.uploadName
|
||||||
else:
|
else:
|
||||||
mimeType = fileObj[2]
|
mimeType = fileObj[2]
|
||||||
|
fileName = fileObj[0]
|
||||||
self.mimeType = mimeType or File.defaultMimeType
|
self.mimeType = mimeType or File.defaultMimeType
|
||||||
# Determine the original name of the file to store.
|
|
||||||
fileName= fileType.startswith('File') and fileObj.filename or fileObj[0]
|
|
||||||
if not fileName:
|
if not fileName:
|
||||||
# Name it according to field name. Deduce file extension from the
|
# Name it according to field name. Deduce file extension from the
|
||||||
# MIME type.
|
# MIME type.
|
||||||
|
@ -195,12 +200,12 @@ class FileInfo:
|
||||||
fsName = osPathJoin(dbFolder, self.fsPath, self.fsName)
|
fsName = osPathJoin(dbFolder, self.fsPath, self.fsName)
|
||||||
f = file(fsName, 'wb')
|
f = file(fsName, 'wb')
|
||||||
if fileType == 'FileUpload':
|
if fileType == 'FileUpload':
|
||||||
# Write the FileUpload instance on disk.
|
# Write the FileUpload instance on disk
|
||||||
self.size = self.replicateFile(fileObj, f)
|
self.size = self.replicateFile(fileObj, f)
|
||||||
elif fileType == 'File':
|
elif fileType == 'File':
|
||||||
# Write the File instance on disk.
|
# Write the File instance on disk
|
||||||
if fileObj.data.__class__.__name__ == 'Pdata':
|
if fileObj.data.__class__.__name__ == 'Pdata':
|
||||||
# The file content is splitted in several chunks.
|
# The file content is splitted in several chunks
|
||||||
f.write(fileObj.data.data)
|
f.write(fileObj.data.data)
|
||||||
nextPart = fileObj.data.next
|
nextPart = fileObj.data.next
|
||||||
while nextPart:
|
while nextPart:
|
||||||
|
@ -210,10 +215,14 @@ class FileInfo:
|
||||||
# Only one chunk
|
# Only one chunk
|
||||||
f.write(fileObj.data)
|
f.write(fileObj.data)
|
||||||
self.size = fileObj.size
|
self.size = fileObj.size
|
||||||
|
elif fileType == 'FileInfo':
|
||||||
|
src = file(fileObj.fsPath, 'rb')
|
||||||
|
self.size = self.replicateFile(src, f)
|
||||||
|
src.close()
|
||||||
else:
|
else:
|
||||||
# Write fileObj[1] on disk.
|
# Write fileObj[1] on disk
|
||||||
if fileObj[1].__class__.__name__ == 'file':
|
if fileObj[1].__class__.__name__ == 'file':
|
||||||
# It is an open file handler.
|
# It is an open file handler
|
||||||
self.size = self.replicateFile(fileObj[1], f)
|
self.size = self.replicateFile(fileObj[1], f)
|
||||||
else:
|
else:
|
||||||
# We have file content directly in fileObj[1]
|
# We have file content directly in fileObj[1]
|
||||||
|
@ -361,7 +370,17 @@ class File(Field):
|
||||||
name = requestName or self.name
|
name = requestName or self.name
|
||||||
return obj.REQUEST.get('%s_file' % name)
|
return obj.REQUEST.get('%s_file' % name)
|
||||||
|
|
||||||
def getCopyValue(self, obj): raise Exception('Not implemented yet.')
|
def getCopyValue(self, obj):
|
||||||
|
'''Create a copy of the FileInfo instance stored for p_obj for this
|
||||||
|
field. This copy will contain the absolute path to the file on the
|
||||||
|
filesystem. This way, the file may be read independently from p_obj
|
||||||
|
(and copied somewhere else).'''
|
||||||
|
info = self.getValue(obj)
|
||||||
|
if not info: return
|
||||||
|
# Create a "not-in-DB", temporary FileInfo
|
||||||
|
return FileInfo(info.getFilePath(obj), inDb=False,
|
||||||
|
uploadName=info.uploadName)
|
||||||
|
|
||||||
def getDefaultLayouts(self): return {'view':'l-f','edit':'lrv-f'}
|
def getDefaultLayouts(self): return {'view':'l-f','edit':'lrv-f'}
|
||||||
|
|
||||||
def isEmptyValue(self, obj, value):
|
def isEmptyValue(self, obj, value):
|
||||||
|
@ -405,13 +424,15 @@ class File(Field):
|
||||||
f. a 3-tuple (fileName, fileContent, mimeType) where
|
f. a 3-tuple (fileName, fileContent, mimeType) where
|
||||||
- fileName and fileContent have the same meaning than above;
|
- fileName and fileContent have the same meaning than above;
|
||||||
- mimeType is the MIME type of the file.
|
- mimeType is the MIME type of the file.
|
||||||
|
g. a FileInfo instance, that must be "not-in-DB", ie, with an
|
||||||
|
absolute path in attribute fsPath.
|
||||||
'''
|
'''
|
||||||
zobj = obj.o
|
zobj = obj.o
|
||||||
if value:
|
if value:
|
||||||
# There is a new value to store. Get the folder on disk where to
|
# There is a new value to store. Get the folder on disk where to
|
||||||
# store the new file.
|
# store the new file.
|
||||||
dbFolder, folder = zobj.getFsFolder(create=True)
|
dbFolder, folder = zobj.getFsFolder(create=True)
|
||||||
# Remove the previous file if it existed.
|
# Remove the previous file if it existed
|
||||||
info = getattr(obj.aq_base, self.name, None)
|
info = getattr(obj.aq_base, self.name, None)
|
||||||
if info:
|
if info:
|
||||||
# The previous file can be a legacy File object in an old
|
# The previous file can be a legacy File object in an old
|
||||||
|
@ -432,6 +453,9 @@ class File(Field):
|
||||||
elif isinstance(value, basestring):
|
elif isinstance(value, basestring):
|
||||||
# Case d
|
# Case d
|
||||||
info.copyFile(self.name, value, dbFolder)
|
info.copyFile(self.name, value, dbFolder)
|
||||||
|
elif isinstance(value, FileInfo):
|
||||||
|
# Case g
|
||||||
|
info.writeFile(self.name, value, dbFolder)
|
||||||
else:
|
else:
|
||||||
# Cases e, f. Extract file name, content and MIME type.
|
# Cases e, f. Extract file name, content and MIME type.
|
||||||
fileName = mimeType = None
|
fileName = mimeType = None
|
||||||
|
@ -444,7 +468,7 @@ class File(Field):
|
||||||
mimeType = mimeType or guessMimeType(fileName)
|
mimeType = mimeType or guessMimeType(fileName)
|
||||||
info.writeFile(self.name, (fileName, fileContent, mimeType),
|
info.writeFile(self.name, (fileName, fileContent, mimeType),
|
||||||
dbFolder)
|
dbFolder)
|
||||||
# Store the FileInfo instance in the database.
|
# Store the FileInfo instance in the database
|
||||||
setattr(obj, self.name, info)
|
setattr(obj, self.name, info)
|
||||||
else:
|
else:
|
||||||
# I store value "None", excepted if I find in the request the desire
|
# I store value "None", excepted if I find in the request the desire
|
||||||
|
|
134
fields/ref.py
134
fields/ref.py
|
@ -77,65 +77,67 @@ class Ref(Field):
|
||||||
# This PX displays icons for triggering actions on a given referenced object
|
# This PX displays icons for triggering actions on a given referenced object
|
||||||
# (edit, delete, etc).
|
# (edit, delete, etc).
|
||||||
pxObjectActions = Px('''
|
pxObjectActions = Px('''
|
||||||
<table class="noStyle">
|
<div>
|
||||||
<tr>
|
<!-- Arrows for moving objects up or down -->
|
||||||
<!-- Arrows for moving objects up or down -->
|
<x if="(totalNumber >1) and changeOrder and not inPickList \
|
||||||
<td if="(totalNumber >1) and changeOrder and not inPickList \
|
and not inMenu"
|
||||||
and not inMenu"
|
var2="ajaxBaseCall=navBaseCall.replace('**v**','%s,%s,{%s:%s,%s:%s}'%\
|
||||||
var2="ajaxBaseCall=navBaseCall.replace('**v**','%s,%s,{%s:%s,%s:%s}'%\
|
(q(startNumber), q('doChangeOrder'), q('refObjectUid'),
|
||||||
(q(startNumber), q('doChangeOrder'), q('refObjectUid'),
|
q(tiedUid), q('move'), q('**v**')))">
|
||||||
q(tiedUid), q('move'), q('**v**')))">
|
<!-- Move to top -->
|
||||||
<!-- Move to top -->
|
<img if="objectIndex > 1" class="clickable"
|
||||||
<img if="objectIndex > 1" class="clickable"
|
src=":url('arrowsUp')" title=":_('move_top')"
|
||||||
src=":url('arrowsUp')" title=":_('move_top')"
|
onclick=":ajaxBaseCall.replace('**v**', 'top')"/>
|
||||||
onclick=":ajaxBaseCall.replace('**v**', 'top')"/>
|
<!-- Move to bottom -->
|
||||||
<!-- Move to bottom -->
|
<img if="objectIndex < (totalNumber-2)" class="clickable"
|
||||||
<img if="objectIndex < (totalNumber-2)" class="clickable"
|
src=":url('arrowsDown')" title=":_('move_bottom')"
|
||||||
src=":url('arrowsDown')" title=":_('move_bottom')"
|
onclick=":ajaxBaseCall.replace('**v**', 'bottom')"/>
|
||||||
onclick=":ajaxBaseCall.replace('**v**', 'bottom')"/>
|
<!-- Move up -->
|
||||||
<!-- Move up -->
|
<img if="objectIndex > 0" class="clickable" src=":url('arrowUp')"
|
||||||
<img if="objectIndex > 0" class="clickable" src=":url('arrowUp')"
|
title=":_('move_up')"
|
||||||
title=":_('move_up')"
|
onclick=":ajaxBaseCall.replace('**v**', 'up')"/>
|
||||||
onclick=":ajaxBaseCall.replace('**v**', 'up')"/>
|
<!-- Move down -->
|
||||||
<!-- Move down -->
|
<img if="objectIndex < (totalNumber-1)" class="clickable"
|
||||||
<img if="objectIndex < (totalNumber-1)" class="clickable"
|
src=":url('arrowDown')" title=":_('move_down')"
|
||||||
src=":url('arrowDown')" title=":_('move_down')"
|
onclick=":ajaxBaseCall.replace('**v**', 'down')"/>
|
||||||
onclick=":ajaxBaseCall.replace('**v**', 'down')"/>
|
</x>
|
||||||
</td>
|
<!-- Edit -->
|
||||||
<!-- Edit -->
|
<a if="not field.noForm and tied.o.mayEdit()"
|
||||||
<td if="not field.noForm and tied.o.mayEdit()">
|
var2="navInfo=field.getNavInfo(zobj, loop.tied.nb + 1 + startNumber, \
|
||||||
<a var="navInfo=field.getNavInfo(zobj, loop.tied.nb + 1 + startNumber, \
|
totalNumber);
|
||||||
totalNumber);
|
linkInPopup=inPopup or (target.target != '_self')"
|
||||||
linkInPopup=inPopup or (target.target != '_self')"
|
href=":tied.o.getUrl(mode='edit', page='main', nav=navInfo, \
|
||||||
href=":tied.o.getUrl(mode='edit', page='main', nav=navInfo, \
|
inPopup=linkInPopup)"
|
||||||
inPopup=linkInPopup)"
|
target=":target.target" onclick=":target.openPopup">
|
||||||
target=":target.target" onclick=":target.openPopup">
|
<img src=":url('edit')" title=":_('object_edit')"/>
|
||||||
<img src=":url('edit')" title=":_('object_edit')"/></a>
|
</a>
|
||||||
</td>
|
<!-- Delete -->
|
||||||
<!-- Delete -->
|
<img if="mayEdit and field.delete and tied.o.mayDelete()"
|
||||||
<td if="mayEdit and field.delete and tied.o.mayDelete()">
|
class="clickable" title=":_('object_delete')" src=":url('delete')"
|
||||||
<img class="clickable" title=":_('object_delete')" src=":url('delete')"
|
onclick=":'onDeleteObject(%s)' % q(tiedUid)"/>
|
||||||
onclick=":'onDeleteObject(%s)' % q(tiedUid)"/>
|
<!-- Unlink -->
|
||||||
</td>
|
<img if="mayUnlink and field.mayUnlinkElement(obj, tied)"
|
||||||
<!-- Unlink -->
|
var2="imgName=linkList and 'unlinkUp' or 'unlink'; action='unlink'"
|
||||||
<td if="mayUnlink and field.mayUnlinkElement(obj, tied)">
|
class="clickable" title=":_('object_unlink')" src=":url(imgName)"
|
||||||
<img var="imgName=linkList and 'unlinkUp' or 'unlink'; action='unlink'"
|
onclick=":'onLink(%s,%s,%s,%s)' % (q(action), q(zobj.id), \
|
||||||
class="clickable" title=":_('object_unlink')" src=":url(imgName)"
|
q(field.name), q(tiedUid))"/>
|
||||||
onclick=":'onLink(%s,%s,%s,%s)' % (q(action), q(zobj.id), \
|
<!-- Insert (if in pick list) -->
|
||||||
q(field.name), q(tiedUid))"/>
|
<img if="inPickList" var2="action='link'" class="clickable"
|
||||||
</td>
|
title=":_('object_link')" src=":url(action)"
|
||||||
<!-- Insert (if in pick list) -->
|
onclick=":'onLink(%s,%s,%s,%s)' % (q(action), q(zobj.id), \
|
||||||
<td if="inPickList">
|
q(field.name), q(tiedUid))"/>
|
||||||
<img var="action='link'" class="clickable" title=":_('object_link')"
|
<!-- Workflow transitions -->
|
||||||
src=":url(action)"
|
<x if="tied.o.showTransitions('result')"
|
||||||
onclick=":'onLink(%s,%s,%s,%s)' % (q(action), q(zobj.id), \
|
var2="targetObj=tied.o; buttonsMode='small'">:tied.pxTransitions</x>
|
||||||
q(field.name), q(tiedUid))"/>
|
<!-- Fields (actions) defined with layout "buttons" -->
|
||||||
</td>
|
<x if="not inPopup"
|
||||||
<!-- Workflow transitions -->
|
var2="fields=tied.o.getAppyTypes('buttons', 'main', type='Action');
|
||||||
<td if="tied.o.showTransitions('result')"
|
layoutType='view'">
|
||||||
var2="targetObj=tied.o; buttonsMode='small'">:tied.pxTransitions</td>
|
<!-- Call pxView and not pxRender to avoid having a table -->
|
||||||
</tr>
|
<x for="field in fields"
|
||||||
</table>''')
|
var2="name=field.name; smallButtons=True">:field.pxView</x>
|
||||||
|
</x>
|
||||||
|
</div>''')
|
||||||
|
|
||||||
# Displays the button allowing to add a new object through a Ref field, if
|
# Displays the button allowing to add a new object through a Ref field, if
|
||||||
# it has been declared as addable and if multiplicities allow it.
|
# it has been declared as addable and if multiplicities allow it.
|
||||||
|
@ -797,7 +799,7 @@ class Ref(Field):
|
||||||
UIDs, because m_store on the destination object can store tied
|
UIDs, because m_store on the destination object can store tied
|
||||||
objects based on such a list.'''
|
objects based on such a list.'''
|
||||||
res = getattr(obj.aq_base, self.name, ())
|
res = getattr(obj.aq_base, self.name, ())
|
||||||
# Return a copy: it can be dangerous to give the real database value.
|
# Return a copy: it can be dangerous to give the real database value
|
||||||
if res: return list(res)
|
if res: return list(res)
|
||||||
|
|
||||||
def getXmlValue(self, obj, value):
|
def getXmlValue(self, obj, value):
|
||||||
|
@ -830,7 +832,7 @@ class Ref(Field):
|
||||||
paginated = startNumber != None
|
paginated = startNumber != None
|
||||||
isSearch = False
|
isSearch = False
|
||||||
if 'masterValues' in req:
|
if 'masterValues' in req:
|
||||||
# Convert masterValue(s) from id(s) to real object(s).
|
# Convert masterValue(s) from id(s) to real object(s)
|
||||||
masterValues = req['masterValues'].strip()
|
masterValues = req['masterValues'].strip()
|
||||||
if not masterValues: masterValues = None
|
if not masterValues: masterValues = None
|
||||||
else:
|
else:
|
||||||
|
@ -865,11 +867,11 @@ class Ref(Field):
|
||||||
objects.objects = [o.appy() for o in objects.objects]
|
objects.objects = [o.appy() for o in objects.objects]
|
||||||
else:
|
else:
|
||||||
objects = self.select(obj)
|
objects = self.select(obj)
|
||||||
# Remove already linked objects if required.
|
# Remove already linked objects if required
|
||||||
if removeLinked:
|
if removeLinked:
|
||||||
uids = getattr(obj.o.aq_base, self.name, None)
|
uids = getattr(obj.o.aq_base, self.name, None)
|
||||||
if uids:
|
if uids:
|
||||||
# Browse objects in reverse order and remove linked objects.
|
# Browse objects in reverse order and remove linked objects
|
||||||
if isSearch: objs = objects.objects
|
if isSearch: objs = objects.objects
|
||||||
else: objs = objects
|
else: objs = objects
|
||||||
i = len(objs) - 1
|
i = len(objs) - 1
|
||||||
|
@ -884,7 +886,7 @@ class Ref(Field):
|
||||||
if paginated and not isSearch:
|
if paginated and not isSearch:
|
||||||
total = len(objects)
|
total = len(objects)
|
||||||
objects = objects[startNumber:startNumber + self.maxPerPage]
|
objects = objects[startNumber:startNumber + self.maxPerPage]
|
||||||
# Return the result, wrapped in a SomeObjects instance if required.
|
# Return the result, wrapped in a SomeObjects instance if required
|
||||||
if not someObjects:
|
if not someObjects:
|
||||||
if isSearch: return objects.objects
|
if isSearch: return objects.objects
|
||||||
return objects
|
return objects
|
||||||
|
@ -909,7 +911,7 @@ class Ref(Field):
|
||||||
for tied in objects:
|
for tied in objects:
|
||||||
menuId = self.menuIdMethod(obj, tied)
|
menuId = self.menuIdMethod(obj, tied)
|
||||||
if menuId in menuIds:
|
if menuId in menuIds:
|
||||||
# We have already encountered this menu.
|
# We have already encountered this menu
|
||||||
menuIndex = menuIds[menuId]
|
menuIndex = menuIds[menuId]
|
||||||
res[menuIndex].objects.append(tied)
|
res[menuIndex].objects.append(tied)
|
||||||
else:
|
else:
|
||||||
|
@ -947,7 +949,7 @@ class Ref(Field):
|
||||||
'''This method returns the index of the first linked object that must be
|
'''This method returns the index of the first linked object that must be
|
||||||
shown, or None if all linked objects must be shown at once (it
|
shown, or None if all linked objects must be shown at once (it
|
||||||
happens when p_render is "menus").'''
|
happens when p_render is "menus").'''
|
||||||
# When using 'menus' render mode, all linked objects must be shown.
|
# When using 'menus' render mode, all linked objects must be shown
|
||||||
if render == 'menus': return
|
if render == 'menus': return
|
||||||
# When using 'list' (=default) render mode, the index of the first
|
# When using 'list' (=default) render mode, the index of the first
|
||||||
# object to show is in the request.
|
# object to show is in the request.
|
||||||
|
|
|
@ -328,7 +328,7 @@ class Transition:
|
||||||
# functions return True.
|
# functions return True.
|
||||||
hasRole = None
|
hasRole = None
|
||||||
for condition in self.condition:
|
for condition in self.condition:
|
||||||
# "Unwrap" role names from Role instances.
|
# "Unwrap" role names from Role instances
|
||||||
if isinstance(condition, Role): condition = condition.name
|
if isinstance(condition, Role): condition = condition.name
|
||||||
if isinstance(condition, basestring): # It is a role
|
if isinstance(condition, basestring): # It is a role
|
||||||
if hasRole == None:
|
if hasRole == None:
|
||||||
|
@ -337,7 +337,7 @@ class Transition:
|
||||||
hasRole = True
|
hasRole = True
|
||||||
else: # It is a method
|
else: # It is a method
|
||||||
res = condition(wf, obj.appy())
|
res = condition(wf, obj.appy())
|
||||||
if not res: return res # False or a No instance.
|
if not res: return res # False or a No instance
|
||||||
if hasRole != False:
|
if hasRole != False:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@ -345,13 +345,13 @@ class Transition:
|
||||||
'''Executes the action related to this transition.'''
|
'''Executes the action related to this transition.'''
|
||||||
msg = ''
|
msg = ''
|
||||||
obj = obj.appy()
|
obj = obj.appy()
|
||||||
wf = wf.__instance__ # We need the prototypical instance here.
|
wf = wf.__instance__ # We need the prototypical instance here
|
||||||
if type(self.action) in (tuple, list):
|
if type(self.action) in (tuple, list):
|
||||||
# We need to execute a list of actions
|
# We need to execute a list of actions
|
||||||
for act in self.action:
|
for act in self.action:
|
||||||
msgPart = act(wf, obj)
|
msgPart = act(wf, obj)
|
||||||
if msgPart: msg += msgPart
|
if msgPart: msg += msgPart
|
||||||
else: # We execute a single action only.
|
else: # We execute a single action only
|
||||||
msgPart = self.action(wf, obj)
|
msgPart = self.action(wf, obj)
|
||||||
if msgPart: msg += msgPart
|
if msgPart: msg += msgPart
|
||||||
return msg
|
return msg
|
||||||
|
|
|
@ -365,30 +365,34 @@ class ToolWrapper(AbstractWrapper):
|
||||||
if="sub">::zobj.highlight(sub)</span>
|
if="sub">::zobj.highlight(sub)</span>
|
||||||
|
|
||||||
<!-- Actions -->
|
<!-- Actions -->
|
||||||
<table class="noStyle" if="not inPopup and zobj.mayAct()">
|
<div if="not inPopup and zobj.mayAct()">
|
||||||
<tr>
|
<!-- Edit -->
|
||||||
<!-- Edit -->
|
<a if="zobj.mayEdit()"
|
||||||
<td if="zobj.mayEdit()">
|
var2="navInfo='search.%s.%s.%d.%d' % \
|
||||||
<a var="navInfo='search.%s.%s.%d.%d' % \
|
|
||||||
(className, searchName, loop.zobj.nb+1+startNumber, totalNumber);
|
(className, searchName, loop.zobj.nb+1+startNumber, totalNumber);
|
||||||
linkInPopup=inPopup or (target.target != '_self')"
|
linkInPopup=inPopup or (target.target != '_self')"
|
||||||
target=":target.target" onclick=":target.openPopup"
|
target=":target.target" onclick=":target.openPopup"
|
||||||
href=":zobj.getUrl(mode='edit', page=zobj.getDefaultEditPage(), \
|
href=":zobj.getUrl(mode='edit', page=zobj.getDefaultEditPage(), \
|
||||||
nav=navInfo, inPopup=linkInPopup)">
|
nav=navInfo, inPopup=linkInPopup)">
|
||||||
<img src=":url('edit')" title=":_('object_edit')"/></a>
|
<img src=":url('edit')" title=":_('object_edit')"/>
|
||||||
</td>
|
</a>
|
||||||
<td>
|
<!-- Delete -->
|
||||||
<!-- Delete -->
|
<img if="zobj.mayDelete()" class="clickable" src=":url('delete')"
|
||||||
<img if="zobj.mayDelete()" class="clickable" src=":url('delete')"
|
title=":_('object_delete')"
|
||||||
title=":_('object_delete')"
|
onClick=":'onDeleteObject(%s)' % q(zobj.id)"/>
|
||||||
onClick=":'onDeleteObject(%s)' % q(zobj.id)"/>
|
<!-- Workflow transitions -->
|
||||||
</td>
|
<x if="zobj.showTransitions('result')"
|
||||||
<!-- Workflow transitions -->
|
var2="targetObj=zobj;
|
||||||
<td if="zobj.showTransitions('result')"
|
buttonsMode='small'">:targetObj.appy().pxTransitions</x>
|
||||||
var2="targetObj=zobj;
|
<!-- Fields (actions) defined with layout "buttons" -->
|
||||||
buttonsMode='small'">:targetObj.appy().pxTransitions</td>
|
<x if="not inPopup"
|
||||||
</tr>
|
var2="fields=zobj.getAppyTypes('buttons', 'main', type='Action');
|
||||||
</table>
|
layoutType='view'">
|
||||||
|
<!-- Call pxView and not pxRender to avoid having a table -->
|
||||||
|
<x for="field in fields"
|
||||||
|
var2="name=field.name; smallButtons=True">:field.pxView</x>
|
||||||
|
</x>
|
||||||
|
</div>
|
||||||
</x>
|
</x>
|
||||||
<x if="not mayView">
|
<x if="not mayView">
|
||||||
<img src=":url('fake')" style="margin-right: 5px"/>
|
<img src=":url('fake')" style="margin-right: 5px"/>
|
||||||
|
|
|
@ -304,7 +304,7 @@ class UserWrapper(AbstractWrapper):
|
||||||
rq = self.request
|
rq = self.request
|
||||||
if (self.user == self) and hasattr(rq, 'userLogins'):
|
if (self.user == self) and hasattr(rq, 'userLogins'):
|
||||||
return rq.userLogins
|
return rq.userLogins
|
||||||
# Compute it.
|
# Compute it
|
||||||
res = [group.login for group in self.groups]
|
res = [group.login for group in self.groups]
|
||||||
if not groupsOnly: res.append(self.login)
|
if not groupsOnly: res.append(self.login)
|
||||||
return res
|
return res
|
||||||
|
|
|
@ -30,7 +30,7 @@ class AbstractWrapper(object):
|
||||||
'(%s).click()' % q(gotoName)"/><img
|
'(%s).click()' % q(gotoName)"/><img
|
||||||
id=":gotoName" name=":gotoName"
|
id=":gotoName" name=":gotoName"
|
||||||
class="clickable" src=":url('gotoNumber')" title=":label"
|
class="clickable" src=":url('gotoNumber')" title=":label"
|
||||||
onClick=":'gotoTied(%s,%s,this.previousSibling,%s)' % \
|
onclick=":'gotoTied(%s,%s,this.previousSibling,%s)' % \
|
||||||
(q(sourceUrl), q(field.name), totalNumber)"/></x>''')
|
(q(sourceUrl), q(field.name), totalNumber)"/></x>''')
|
||||||
|
|
||||||
pxNavigationStrip = Px('''
|
pxNavigationStrip = Px('''
|
||||||
|
@ -469,7 +469,7 @@ class AbstractWrapper(object):
|
||||||
<!-- Button on the edit page -->
|
<!-- Button on the edit page -->
|
||||||
<x if="isEdit">
|
<x if="isEdit">
|
||||||
<input type="button" class="button" value=":label"
|
<input type="button" class="button" value=":label"
|
||||||
onClick="submitAppyForm('previous')"
|
onclick="submitAppyForm('previous')"
|
||||||
style=":'%s; %s' % (url('previous', bg=True), buttonWidth)"/>
|
style=":'%s; %s' % (url('previous', bg=True), buttonWidth)"/>
|
||||||
<input type="hidden" name="previousPage" value=":previousPage"/>
|
<input type="hidden" name="previousPage" value=":previousPage"/>
|
||||||
</x>
|
</x>
|
||||||
|
@ -479,26 +479,22 @@ class AbstractWrapper(object):
|
||||||
onclick=":'goto(%s)' % q(zobj.getUrl(page=previousPage, \
|
onclick=":'goto(%s)' % q(zobj.getUrl(page=previousPage, \
|
||||||
inPopup=inPopup))"/>
|
inPopup=inPopup))"/>
|
||||||
</x>
|
</x>
|
||||||
|
|
||||||
<!-- Save -->
|
<!-- Save -->
|
||||||
<input if="isEdit and pageInfo.showSave"
|
<input if="isEdit and pageInfo.showSave"
|
||||||
type="button" class="button" onClick="submitAppyForm('save')"
|
type="button" class="button" onclick="submitAppyForm('save')"
|
||||||
var2="label=_('object_save')" value=":label"
|
var2="label=_('object_save')" value=":label"
|
||||||
style=":'%s; %s' % (url('save', bg=True), \
|
style=":'%s; %s' % (url('save', bg=True), \
|
||||||
ztool.getButtonWidth(label))" />
|
ztool.getButtonWidth(label))" />
|
||||||
|
|
||||||
<!-- Cancel -->
|
<!-- Cancel -->
|
||||||
<input if="isEdit and pageInfo.showCancel"
|
<input if="isEdit and pageInfo.showCancel"
|
||||||
type="button" class="button" onClick="submitAppyForm('cancel')"
|
type="button" class="button" onclick="submitAppyForm('cancel')"
|
||||||
var2="label=_('object_cancel')" value=":label"
|
var2="label=_('object_cancel')" value=":label"
|
||||||
style=":'%s; %s' % (url('cancel', bg=True), \
|
style=":'%s; %s' % (url('cancel', bg=True), \
|
||||||
ztool.getButtonWidth(label))"/>
|
ztool.getButtonWidth(label))"/>
|
||||||
|
|
||||||
<x if="not isEdit"
|
<x if="not isEdit"
|
||||||
var2="locked=zobj.isLocked(user, page);
|
var2="locked=zobj.isLocked(user, page);
|
||||||
editable=pageInfo.showOnEdit and pageInfo.showEdit and \
|
editable=pageInfo.showOnEdit and pageInfo.showEdit and \
|
||||||
mayAct and zobj.mayEdit()">
|
mayAct and zobj.mayEdit()">
|
||||||
|
|
||||||
<!-- Edit -->
|
<!-- Edit -->
|
||||||
<input type="button" class="button" if="editable and not locked"
|
<input type="button" class="button" if="editable and not locked"
|
||||||
var="label=_('object_edit')" value=":label"
|
var="label=_('object_edit')" value=":label"
|
||||||
|
@ -506,7 +502,6 @@ class AbstractWrapper(object):
|
||||||
ztool.getButtonWidth(label))"
|
ztool.getButtonWidth(label))"
|
||||||
onclick=":'goto(%s)' % q(zobj.getUrl(mode='edit', page=page, \
|
onclick=":'goto(%s)' % q(zobj.getUrl(mode='edit', page=page, \
|
||||||
inPopup=inPopup))"/>
|
inPopup=inPopup))"/>
|
||||||
|
|
||||||
<!-- Locked -->
|
<!-- Locked -->
|
||||||
<a if="editable and locked">
|
<a if="editable and locked">
|
||||||
<img style="cursor: help"
|
<img style="cursor: help"
|
||||||
|
@ -520,14 +515,20 @@ class AbstractWrapper(object):
|
||||||
src=":url('unlockBig')"
|
src=":url('unlockBig')"
|
||||||
onclick=":'onUnlockPage(%s,%s)' % (q(zobj.id), q(page))"/></a>
|
onclick=":'onUnlockPage(%s,%s)' % (q(zobj.id), q(page))"/></a>
|
||||||
</x>
|
</x>
|
||||||
|
<!-- Delete -->
|
||||||
|
<input if="not isEdit and not inPopup and zobj.mayDelete()"
|
||||||
|
type="button" class="button"
|
||||||
|
onclick=":'onDeleteObject(%s)' % q(zobj.id)"
|
||||||
|
var2="label=_('object_delete')" value=":label"
|
||||||
|
style=":'%s; %s' % (url('delete', bg=True), \
|
||||||
|
ztool.getButtonWidth(label))"/>
|
||||||
<!-- Next -->
|
<!-- Next -->
|
||||||
<x if="nextPage and pageInfo.showNext"
|
<x if="nextPage and pageInfo.showNext"
|
||||||
var2="label=_('page_next');
|
var2="label=_('page_next');
|
||||||
buttonWidth=ztool.getButtonWidth(label)">
|
buttonWidth=ztool.getButtonWidth(label)">
|
||||||
<!-- Button on the edit page -->
|
<!-- Button on the edit page -->
|
||||||
<x if="isEdit">
|
<x if="isEdit">
|
||||||
<input type="button" class="button" onClick="submitAppyForm('next')"
|
<input type="button" class="button" onclick="submitAppyForm('next')"
|
||||||
style=":'%s; %s' % (url('next', bg=True), buttonWidth)"
|
style=":'%s; %s' % (url('next', bg=True), buttonWidth)"
|
||||||
value=":label"/>
|
value=":label"/>
|
||||||
<input type="hidden" name="nextPage" value=":nextPage"/>
|
<input type="hidden" name="nextPage" value=":nextPage"/>
|
||||||
|
@ -538,12 +539,10 @@ class AbstractWrapper(object):
|
||||||
onclick=":'goto(%s)' % q(zobj.getUrl(page=nextPage, \
|
onclick=":'goto(%s)' % q(zobj.getUrl(page=nextPage, \
|
||||||
inPopup=inPopup))"/>
|
inPopup=inPopup))"/>
|
||||||
</x>
|
</x>
|
||||||
|
|
||||||
<!-- Workflow transitions -->
|
<!-- Workflow transitions -->
|
||||||
<x var="targetObj=zobj; buttonsMode='normal'"
|
<x var="targetObj=zobj; buttonsMode='normal'"
|
||||||
if="mayAct and \
|
if="mayAct and \
|
||||||
targetObj.showTransitions(layoutType)">:obj.pxTransitions</x>
|
targetObj.showTransitions(layoutType)">:obj.pxTransitions</x>
|
||||||
|
|
||||||
<!-- Fields (actions) defined with layout "buttons" -->
|
<!-- Fields (actions) defined with layout "buttons" -->
|
||||||
<x if="layoutType != 'edit'"
|
<x if="layoutType != 'edit'"
|
||||||
var2="fields=zobj.getAppyTypes('buttons', page, type='Action');
|
var2="fields=zobj.getAppyTypes('buttons', page, type='Action');
|
||||||
|
@ -929,13 +928,17 @@ class AbstractWrapper(object):
|
||||||
return appyObj
|
return appyObj
|
||||||
|
|
||||||
def createFrom(self, fieldNameOrClass, other, noSecurity=False,
|
def createFrom(self, fieldNameOrClass, other, noSecurity=False,
|
||||||
executeMethods=True, exclude=()):
|
executeMethods=True, exclude=(), keepBase=False):
|
||||||
'''Similar to m_create above, excepted that we will use another object
|
'''Similar to m_create above, excepted that we will use another object
|
||||||
(p_other) as base for filling in data for the object to create.
|
(p_other) as base for filling in data for the object to create.
|
||||||
p_exclude can list fields (by their names) that will not be
|
p_exclude can list fields (by their names) that will not be copied on
|
||||||
copied on p_other. Note that this method does not perform a deep
|
p_other. If p_keepBase is True, basic attributes will be kept on the
|
||||||
copy: objects linked via Ref fields from p_self will be
|
new object: creator and dates "created" and "modified". Else, the
|
||||||
referenced by the clone, but not themselves copied.'''
|
new object's creator will be the logged user.
|
||||||
|
|
||||||
|
Note that this method does not perform a deep copy: objects linked
|
||||||
|
via Ref fields from p_self will be referenced by the clone, but not
|
||||||
|
themselves copied.'''
|
||||||
# Get the field values to set from p_other and store it in a dict.
|
# Get the field values to set from p_other and store it in a dict.
|
||||||
# p_other may not be of the same class as p_self.
|
# p_other may not be of the same class as p_self.
|
||||||
params = {}
|
params = {}
|
||||||
|
@ -944,9 +947,14 @@ class AbstractWrapper(object):
|
||||||
if not field.persist or (field.name in exclude) or \
|
if not field.persist or (field.name in exclude) or \
|
||||||
((field.type == 'Ref') and field.isBack): continue
|
((field.type == 'Ref') and field.isBack): continue
|
||||||
params[field.name] = field.getCopyValue(other.o)
|
params[field.name] = field.getCopyValue(other.o)
|
||||||
return self.create(fieldNameOrClass, noSecurity=noSecurity,
|
res = self.create(fieldNameOrClass, noSecurity=noSecurity,
|
||||||
raiseOnWrongAttribute=False,
|
raiseOnWrongAttribute=False,
|
||||||
executeMethods=executeMethods, **params)
|
executeMethods=executeMethods, **params)
|
||||||
|
# Propagate base attributes if required
|
||||||
|
if keepBase:
|
||||||
|
for name in ('creator', 'created', 'modified'):
|
||||||
|
setattr(res.o, name, getattr(other.o, name))
|
||||||
|
return res
|
||||||
|
|
||||||
def freeze(self, fieldName, template=None, format='pdf', noSecurity=True,
|
def freeze(self, fieldName, template=None, format='pdf', noSecurity=True,
|
||||||
freezeOdtOnError=True):
|
freezeOdtOnError=True):
|
||||||
|
|
Loading…
Reference in a new issue