[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);
|
||||
label=_(field.labelId);
|
||||
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'"
|
||||
style="display:inline">
|
||||
<input type="hidden" name="action" value="ExecuteAction"/>
|
||||
<input type="hidden" name="objectUid" value=":zobj.id"/>
|
||||
<input type="hidden" name="fieldName" value=":name"/>
|
||||
<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');
|
||||
commentParam=(field.confirm == 'text') and 'true' or 'false'"
|
||||
value=":label"
|
||||
style=":'%s; %s' % (url(field.icon, bg=True), buttonWidth)"
|
||||
onclick=":'askConfirm(%s,%s,%s,%s)' % (q('form'), q(formId), \
|
||||
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"
|
||||
style=":'%s; %s' % (url(field.icon, bg=True), buttonWidth)"/>
|
||||
</form>''')
|
||||
|
|
|
@ -86,10 +86,10 @@ class FileInfo:
|
|||
self.uploadName = uploadName # The name of the uploaded file
|
||||
self.size = 0 # Its size, in bytes
|
||||
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
|
||||
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
|
||||
# string is equivalent.
|
||||
fileInfo = os.stat(self.fsPath)
|
||||
|
@ -166,20 +166,25 @@ class FileInfo:
|
|||
'''Writes to the filesystem the p_fileObj file, that can be:
|
||||
- a Zope FileUpload (coming from a HTTP post);
|
||||
- a OFS.Image.File object (legacy within-ZODB file object);
|
||||
- another ("not-in-DB") FileInfo instance;
|
||||
- a tuple (fileName, fileContent, mimeType)
|
||||
(see doc in method File.store below).'''
|
||||
# Determine p_fileObj's type
|
||||
fileType = fileObj.__class__.__name__
|
||||
# Set MIME type
|
||||
# Determine the MIME type and the base name of the file to store
|
||||
if fileType == 'FileUpload':
|
||||
mimeType = self.getMimeTypeFromFileUpload(fileObj)
|
||||
fileName = fileObj.filename
|
||||
elif fileType == 'File':
|
||||
mimeType = fileObj.content_type
|
||||
fileName = fileObj.filename
|
||||
elif fileType == 'FileInfo':
|
||||
mimeType = fileObj.mimeType
|
||||
fileName = fileObj.uploadName
|
||||
else:
|
||||
mimeType = fileObj[2]
|
||||
fileName = fileObj[0]
|
||||
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:
|
||||
# Name it according to field name. Deduce file extension from the
|
||||
# MIME type.
|
||||
|
@ -195,12 +200,12 @@ class FileInfo:
|
|||
fsName = osPathJoin(dbFolder, self.fsPath, self.fsName)
|
||||
f = file(fsName, 'wb')
|
||||
if fileType == 'FileUpload':
|
||||
# Write the FileUpload instance on disk.
|
||||
# Write the FileUpload instance on disk
|
||||
self.size = self.replicateFile(fileObj, f)
|
||||
elif fileType == 'File':
|
||||
# Write the File instance on disk.
|
||||
# Write the File instance on disk
|
||||
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)
|
||||
nextPart = fileObj.data.next
|
||||
while nextPart:
|
||||
|
@ -210,10 +215,14 @@ class FileInfo:
|
|||
# Only one chunk
|
||||
f.write(fileObj.data)
|
||||
self.size = fileObj.size
|
||||
elif fileType == 'FileInfo':
|
||||
src = file(fileObj.fsPath, 'rb')
|
||||
self.size = self.replicateFile(src, f)
|
||||
src.close()
|
||||
else:
|
||||
# Write fileObj[1] on disk.
|
||||
# Write fileObj[1] on disk
|
||||
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)
|
||||
else:
|
||||
# We have file content directly in fileObj[1]
|
||||
|
@ -361,7 +370,17 @@ class File(Field):
|
|||
name = requestName or self.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 isEmptyValue(self, obj, value):
|
||||
|
@ -405,13 +424,15 @@ class File(Field):
|
|||
f. a 3-tuple (fileName, fileContent, mimeType) where
|
||||
- fileName and fileContent have the same meaning than above;
|
||||
- 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
|
||||
if value:
|
||||
# There is a new value to store. Get the folder on disk where to
|
||||
# store the new file.
|
||||
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)
|
||||
if info:
|
||||
# The previous file can be a legacy File object in an old
|
||||
|
@ -432,6 +453,9 @@ class File(Field):
|
|||
elif isinstance(value, basestring):
|
||||
# Case d
|
||||
info.copyFile(self.name, value, dbFolder)
|
||||
elif isinstance(value, FileInfo):
|
||||
# Case g
|
||||
info.writeFile(self.name, value, dbFolder)
|
||||
else:
|
||||
# Cases e, f. Extract file name, content and MIME type.
|
||||
fileName = mimeType = None
|
||||
|
@ -444,7 +468,7 @@ class File(Field):
|
|||
mimeType = mimeType or guessMimeType(fileName)
|
||||
info.writeFile(self.name, (fileName, fileContent, mimeType),
|
||||
dbFolder)
|
||||
# Store the FileInfo instance in the database.
|
||||
# Store the FileInfo instance in the database
|
||||
setattr(obj, self.name, info)
|
||||
else:
|
||||
# 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
|
||||
# (edit, delete, etc).
|
||||
pxObjectActions = Px('''
|
||||
<table class="noStyle">
|
||||
<tr>
|
||||
<!-- Arrows for moving objects up or down -->
|
||||
<td if="(totalNumber >1) and changeOrder and not inPickList \
|
||||
and not inMenu"
|
||||
var2="ajaxBaseCall=navBaseCall.replace('**v**','%s,%s,{%s:%s,%s:%s}'%\
|
||||
(q(startNumber), q('doChangeOrder'), q('refObjectUid'),
|
||||
q(tiedUid), q('move'), q('**v**')))">
|
||||
<!-- Move to top -->
|
||||
<img if="objectIndex > 1" class="clickable"
|
||||
src=":url('arrowsUp')" title=":_('move_top')"
|
||||
onclick=":ajaxBaseCall.replace('**v**', 'top')"/>
|
||||
<!-- Move to bottom -->
|
||||
<img if="objectIndex < (totalNumber-2)" class="clickable"
|
||||
src=":url('arrowsDown')" title=":_('move_bottom')"
|
||||
onclick=":ajaxBaseCall.replace('**v**', 'bottom')"/>
|
||||
<!-- Move up -->
|
||||
<img if="objectIndex > 0" class="clickable" src=":url('arrowUp')"
|
||||
title=":_('move_up')"
|
||||
onclick=":ajaxBaseCall.replace('**v**', 'up')"/>
|
||||
<!-- Move down -->
|
||||
<img if="objectIndex < (totalNumber-1)" class="clickable"
|
||||
src=":url('arrowDown')" title=":_('move_down')"
|
||||
onclick=":ajaxBaseCall.replace('**v**', 'down')"/>
|
||||
</td>
|
||||
<!-- Edit -->
|
||||
<td if="not field.noForm and tied.o.mayEdit()">
|
||||
<a var="navInfo=field.getNavInfo(zobj, loop.tied.nb + 1 + startNumber, \
|
||||
totalNumber);
|
||||
linkInPopup=inPopup or (target.target != '_self')"
|
||||
href=":tied.o.getUrl(mode='edit', page='main', nav=navInfo, \
|
||||
inPopup=linkInPopup)"
|
||||
target=":target.target" onclick=":target.openPopup">
|
||||
<img src=":url('edit')" title=":_('object_edit')"/></a>
|
||||
</td>
|
||||
<!-- Delete -->
|
||||
<td if="mayEdit and field.delete and tied.o.mayDelete()">
|
||||
<img class="clickable" title=":_('object_delete')" src=":url('delete')"
|
||||
onclick=":'onDeleteObject(%s)' % q(tiedUid)"/>
|
||||
</td>
|
||||
<!-- Unlink -->
|
||||
<td if="mayUnlink and field.mayUnlinkElement(obj, tied)">
|
||||
<img var="imgName=linkList and 'unlinkUp' or 'unlink'; action='unlink'"
|
||||
class="clickable" title=":_('object_unlink')" src=":url(imgName)"
|
||||
onclick=":'onLink(%s,%s,%s,%s)' % (q(action), q(zobj.id), \
|
||||
q(field.name), q(tiedUid))"/>
|
||||
</td>
|
||||
<!-- Insert (if in pick list) -->
|
||||
<td if="inPickList">
|
||||
<img var="action='link'" class="clickable" title=":_('object_link')"
|
||||
src=":url(action)"
|
||||
onclick=":'onLink(%s,%s,%s,%s)' % (q(action), q(zobj.id), \
|
||||
q(field.name), q(tiedUid))"/>
|
||||
</td>
|
||||
<!-- Workflow transitions -->
|
||||
<td if="tied.o.showTransitions('result')"
|
||||
var2="targetObj=tied.o; buttonsMode='small'">:tied.pxTransitions</td>
|
||||
</tr>
|
||||
</table>''')
|
||||
<div>
|
||||
<!-- Arrows for moving objects up or down -->
|
||||
<x if="(totalNumber >1) and changeOrder and not inPickList \
|
||||
and not inMenu"
|
||||
var2="ajaxBaseCall=navBaseCall.replace('**v**','%s,%s,{%s:%s,%s:%s}'%\
|
||||
(q(startNumber), q('doChangeOrder'), q('refObjectUid'),
|
||||
q(tiedUid), q('move'), q('**v**')))">
|
||||
<!-- Move to top -->
|
||||
<img if="objectIndex > 1" class="clickable"
|
||||
src=":url('arrowsUp')" title=":_('move_top')"
|
||||
onclick=":ajaxBaseCall.replace('**v**', 'top')"/>
|
||||
<!-- Move to bottom -->
|
||||
<img if="objectIndex < (totalNumber-2)" class="clickable"
|
||||
src=":url('arrowsDown')" title=":_('move_bottom')"
|
||||
onclick=":ajaxBaseCall.replace('**v**', 'bottom')"/>
|
||||
<!-- Move up -->
|
||||
<img if="objectIndex > 0" class="clickable" src=":url('arrowUp')"
|
||||
title=":_('move_up')"
|
||||
onclick=":ajaxBaseCall.replace('**v**', 'up')"/>
|
||||
<!-- Move down -->
|
||||
<img if="objectIndex < (totalNumber-1)" class="clickable"
|
||||
src=":url('arrowDown')" title=":_('move_down')"
|
||||
onclick=":ajaxBaseCall.replace('**v**', 'down')"/>
|
||||
</x>
|
||||
<!-- Edit -->
|
||||
<a if="not field.noForm and tied.o.mayEdit()"
|
||||
var2="navInfo=field.getNavInfo(zobj, loop.tied.nb + 1 + startNumber, \
|
||||
totalNumber);
|
||||
linkInPopup=inPopup or (target.target != '_self')"
|
||||
href=":tied.o.getUrl(mode='edit', page='main', nav=navInfo, \
|
||||
inPopup=linkInPopup)"
|
||||
target=":target.target" onclick=":target.openPopup">
|
||||
<img src=":url('edit')" title=":_('object_edit')"/>
|
||||
</a>
|
||||
<!-- Delete -->
|
||||
<img if="mayEdit and field.delete and tied.o.mayDelete()"
|
||||
class="clickable" title=":_('object_delete')" src=":url('delete')"
|
||||
onclick=":'onDeleteObject(%s)' % q(tiedUid)"/>
|
||||
<!-- Unlink -->
|
||||
<img if="mayUnlink and field.mayUnlinkElement(obj, tied)"
|
||||
var2="imgName=linkList and 'unlinkUp' or 'unlink'; action='unlink'"
|
||||
class="clickable" title=":_('object_unlink')" src=":url(imgName)"
|
||||
onclick=":'onLink(%s,%s,%s,%s)' % (q(action), q(zobj.id), \
|
||||
q(field.name), q(tiedUid))"/>
|
||||
<!-- Insert (if in pick list) -->
|
||||
<img if="inPickList" var2="action='link'" class="clickable"
|
||||
title=":_('object_link')" src=":url(action)"
|
||||
onclick=":'onLink(%s,%s,%s,%s)' % (q(action), q(zobj.id), \
|
||||
q(field.name), q(tiedUid))"/>
|
||||
<!-- Workflow transitions -->
|
||||
<x if="tied.o.showTransitions('result')"
|
||||
var2="targetObj=tied.o; buttonsMode='small'">:tied.pxTransitions</x>
|
||||
<!-- Fields (actions) defined with layout "buttons" -->
|
||||
<x if="not inPopup"
|
||||
var2="fields=tied.o.getAppyTypes('buttons', 'main', type='Action');
|
||||
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>''')
|
||||
|
||||
# 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.
|
||||
|
@ -797,7 +799,7 @@ class Ref(Field):
|
|||
UIDs, because m_store on the destination object can store tied
|
||||
objects based on such a list.'''
|
||||
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)
|
||||
|
||||
def getXmlValue(self, obj, value):
|
||||
|
@ -830,7 +832,7 @@ class Ref(Field):
|
|||
paginated = startNumber != None
|
||||
isSearch = False
|
||||
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()
|
||||
if not masterValues: masterValues = None
|
||||
else:
|
||||
|
@ -865,11 +867,11 @@ class Ref(Field):
|
|||
objects.objects = [o.appy() for o in objects.objects]
|
||||
else:
|
||||
objects = self.select(obj)
|
||||
# Remove already linked objects if required.
|
||||
# Remove already linked objects if required
|
||||
if removeLinked:
|
||||
uids = getattr(obj.o.aq_base, self.name, None)
|
||||
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
|
||||
else: objs = objects
|
||||
i = len(objs) - 1
|
||||
|
@ -884,7 +886,7 @@ class Ref(Field):
|
|||
if paginated and not isSearch:
|
||||
total = len(objects)
|
||||
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 isSearch: return objects.objects
|
||||
return objects
|
||||
|
@ -909,7 +911,7 @@ class Ref(Field):
|
|||
for tied in objects:
|
||||
menuId = self.menuIdMethod(obj, tied)
|
||||
if menuId in menuIds:
|
||||
# We have already encountered this menu.
|
||||
# We have already encountered this menu
|
||||
menuIndex = menuIds[menuId]
|
||||
res[menuIndex].objects.append(tied)
|
||||
else:
|
||||
|
@ -947,7 +949,7 @@ class Ref(Field):
|
|||
'''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
|
||||
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
|
||||
# When using 'list' (=default) render mode, the index of the first
|
||||
# object to show is in the request.
|
||||
|
|
|
@ -328,7 +328,7 @@ class Transition:
|
|||
# functions return True.
|
||||
hasRole = None
|
||||
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, basestring): # It is a role
|
||||
if hasRole == None:
|
||||
|
@ -337,7 +337,7 @@ class Transition:
|
|||
hasRole = True
|
||||
else: # It is a method
|
||||
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:
|
||||
return True
|
||||
|
||||
|
@ -345,13 +345,13 @@ class Transition:
|
|||
'''Executes the action related to this transition.'''
|
||||
msg = ''
|
||||
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):
|
||||
# We need to execute a list of actions
|
||||
for act in self.action:
|
||||
msgPart = act(wf, obj)
|
||||
if msgPart: msg += msgPart
|
||||
else: # We execute a single action only.
|
||||
else: # We execute a single action only
|
||||
msgPart = self.action(wf, obj)
|
||||
if msgPart: msg += msgPart
|
||||
return msg
|
||||
|
|
|
@ -365,30 +365,34 @@ class ToolWrapper(AbstractWrapper):
|
|||
if="sub">::zobj.highlight(sub)</span>
|
||||
|
||||
<!-- Actions -->
|
||||
<table class="noStyle" if="not inPopup and zobj.mayAct()">
|
||||
<tr>
|
||||
<!-- Edit -->
|
||||
<td if="zobj.mayEdit()">
|
||||
<a var="navInfo='search.%s.%s.%d.%d' % \
|
||||
<div if="not inPopup and zobj.mayAct()">
|
||||
<!-- Edit -->
|
||||
<a if="zobj.mayEdit()"
|
||||
var2="navInfo='search.%s.%s.%d.%d' % \
|
||||
(className, searchName, loop.zobj.nb+1+startNumber, totalNumber);
|
||||
linkInPopup=inPopup or (target.target != '_self')"
|
||||
target=":target.target" onclick=":target.openPopup"
|
||||
href=":zobj.getUrl(mode='edit', page=zobj.getDefaultEditPage(), \
|
||||
nav=navInfo, inPopup=linkInPopup)">
|
||||
<img src=":url('edit')" title=":_('object_edit')"/></a>
|
||||
</td>
|
||||
<td>
|
||||
<!-- Delete -->
|
||||
<img if="zobj.mayDelete()" class="clickable" src=":url('delete')"
|
||||
title=":_('object_delete')"
|
||||
onClick=":'onDeleteObject(%s)' % q(zobj.id)"/>
|
||||
</td>
|
||||
<!-- Workflow transitions -->
|
||||
<td if="zobj.showTransitions('result')"
|
||||
var2="targetObj=zobj;
|
||||
buttonsMode='small'">:targetObj.appy().pxTransitions</td>
|
||||
</tr>
|
||||
</table>
|
||||
linkInPopup=inPopup or (target.target != '_self')"
|
||||
target=":target.target" onclick=":target.openPopup"
|
||||
href=":zobj.getUrl(mode='edit', page=zobj.getDefaultEditPage(), \
|
||||
nav=navInfo, inPopup=linkInPopup)">
|
||||
<img src=":url('edit')" title=":_('object_edit')"/>
|
||||
</a>
|
||||
<!-- Delete -->
|
||||
<img if="zobj.mayDelete()" class="clickable" src=":url('delete')"
|
||||
title=":_('object_delete')"
|
||||
onClick=":'onDeleteObject(%s)' % q(zobj.id)"/>
|
||||
<!-- Workflow transitions -->
|
||||
<x if="zobj.showTransitions('result')"
|
||||
var2="targetObj=zobj;
|
||||
buttonsMode='small'">:targetObj.appy().pxTransitions</x>
|
||||
<!-- Fields (actions) defined with layout "buttons" -->
|
||||
<x if="not inPopup"
|
||||
var2="fields=zobj.getAppyTypes('buttons', 'main', type='Action');
|
||||
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 if="not mayView">
|
||||
<img src=":url('fake')" style="margin-right: 5px"/>
|
||||
|
|
|
@ -304,7 +304,7 @@ class UserWrapper(AbstractWrapper):
|
|||
rq = self.request
|
||||
if (self.user == self) and hasattr(rq, 'userLogins'):
|
||||
return rq.userLogins
|
||||
# Compute it.
|
||||
# Compute it
|
||||
res = [group.login for group in self.groups]
|
||||
if not groupsOnly: res.append(self.login)
|
||||
return res
|
||||
|
|
|
@ -30,7 +30,7 @@ class AbstractWrapper(object):
|
|||
'(%s).click()' % q(gotoName)"/><img
|
||||
id=":gotoName" name=":gotoName"
|
||||
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>''')
|
||||
|
||||
pxNavigationStrip = Px('''
|
||||
|
@ -469,7 +469,7 @@ class AbstractWrapper(object):
|
|||
<!-- Button on the edit page -->
|
||||
<x if="isEdit">
|
||||
<input type="button" class="button" value=":label"
|
||||
onClick="submitAppyForm('previous')"
|
||||
onclick="submitAppyForm('previous')"
|
||||
style=":'%s; %s' % (url('previous', bg=True), buttonWidth)"/>
|
||||
<input type="hidden" name="previousPage" value=":previousPage"/>
|
||||
</x>
|
||||
|
@ -479,26 +479,22 @@ class AbstractWrapper(object):
|
|||
onclick=":'goto(%s)' % q(zobj.getUrl(page=previousPage, \
|
||||
inPopup=inPopup))"/>
|
||||
</x>
|
||||
|
||||
<!-- Save -->
|
||||
<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"
|
||||
style=":'%s; %s' % (url('save', bg=True), \
|
||||
ztool.getButtonWidth(label))" />
|
||||
|
||||
<!-- Cancel -->
|
||||
<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"
|
||||
style=":'%s; %s' % (url('cancel', bg=True), \
|
||||
ztool.getButtonWidth(label))"/>
|
||||
|
||||
<x if="not isEdit"
|
||||
var2="locked=zobj.isLocked(user, page);
|
||||
editable=pageInfo.showOnEdit and pageInfo.showEdit and \
|
||||
mayAct and zobj.mayEdit()">
|
||||
|
||||
<!-- Edit -->
|
||||
<input type="button" class="button" if="editable and not locked"
|
||||
var="label=_('object_edit')" value=":label"
|
||||
|
@ -506,7 +502,6 @@ class AbstractWrapper(object):
|
|||
ztool.getButtonWidth(label))"
|
||||
onclick=":'goto(%s)' % q(zobj.getUrl(mode='edit', page=page, \
|
||||
inPopup=inPopup))"/>
|
||||
|
||||
<!-- Locked -->
|
||||
<a if="editable and locked">
|
||||
<img style="cursor: help"
|
||||
|
@ -520,14 +515,20 @@ class AbstractWrapper(object):
|
|||
src=":url('unlockBig')"
|
||||
onclick=":'onUnlockPage(%s,%s)' % (q(zobj.id), q(page))"/></a>
|
||||
</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 -->
|
||||
<x if="nextPage and pageInfo.showNext"
|
||||
var2="label=_('page_next');
|
||||
buttonWidth=ztool.getButtonWidth(label)">
|
||||
<!-- Button on the edit page -->
|
||||
<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)"
|
||||
value=":label"/>
|
||||
<input type="hidden" name="nextPage" value=":nextPage"/>
|
||||
|
@ -538,12 +539,10 @@ class AbstractWrapper(object):
|
|||
onclick=":'goto(%s)' % q(zobj.getUrl(page=nextPage, \
|
||||
inPopup=inPopup))"/>
|
||||
</x>
|
||||
|
||||
<!-- Workflow transitions -->
|
||||
<x var="targetObj=zobj; buttonsMode='normal'"
|
||||
if="mayAct and \
|
||||
targetObj.showTransitions(layoutType)">:obj.pxTransitions</x>
|
||||
|
||||
<!-- Fields (actions) defined with layout "buttons" -->
|
||||
<x if="layoutType != 'edit'"
|
||||
var2="fields=zobj.getAppyTypes('buttons', page, type='Action');
|
||||
|
@ -929,13 +928,17 @@ class AbstractWrapper(object):
|
|||
return appyObj
|
||||
|
||||
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
|
||||
(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
|
||||
copied on p_other. 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.'''
|
||||
p_exclude can list fields (by their names) that will not be copied on
|
||||
p_other. If p_keepBase is True, basic attributes will be kept on the
|
||||
new object: creator and dates "created" and "modified". Else, the
|
||||
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.
|
||||
# p_other may not be of the same class as p_self.
|
||||
params = {}
|
||||
|
@ -944,9 +947,14 @@ class AbstractWrapper(object):
|
|||
if not field.persist or (field.name in exclude) or \
|
||||
((field.type == 'Ref') and field.isBack): continue
|
||||
params[field.name] = field.getCopyValue(other.o)
|
||||
return self.create(fieldNameOrClass, noSecurity=noSecurity,
|
||||
raiseOnWrongAttribute=False,
|
||||
executeMethods=executeMethods, **params)
|
||||
res = self.create(fieldNameOrClass, noSecurity=noSecurity,
|
||||
raiseOnWrongAttribute=False,
|
||||
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,
|
||||
freezeOdtOnError=True):
|
||||
|
|
Loading…
Reference in a new issue