Two important bugfixes: one security-related, one linked to Ref fields with link=True.
This commit is contained in:
parent
dbcadc506d
commit
fa974239f3
1
bin/new.py
Executable file → Normal file
1
bin/new.py
Executable file → Normal file
|
@ -1,4 +1,3 @@
|
||||||
#!/usr/bin/python2.4.4
|
|
||||||
'''This script allows to create a brand new read-to-use Plone/Zone instance.
|
'''This script allows to create a brand new read-to-use Plone/Zone instance.
|
||||||
As prerequisite, you must have installed Plone through the Unifier installer
|
As prerequisite, you must have installed Plone through the Unifier installer
|
||||||
available at http://plone.org.'''
|
available at http://plone.org.'''
|
||||||
|
|
|
@ -30,7 +30,7 @@ class Group:
|
||||||
'''Used for describing a group of widgets within a page.'''
|
'''Used for describing a group of widgets within a page.'''
|
||||||
def __init__(self, name, columns=['100%'], wide=True, style='fieldset',
|
def __init__(self, name, columns=['100%'], wide=True, style='fieldset',
|
||||||
hasLabel=True, hasDescr=False, hasHelp=False,
|
hasLabel=True, hasDescr=False, hasHelp=False,
|
||||||
hasHeaders=False, group=None, colspan=1):
|
hasHeaders=False, group=None, colspan=1, valign='top'):
|
||||||
self.name = name
|
self.name = name
|
||||||
# In its simpler form, field "columns" below can hold a list or tuple
|
# In its simpler form, field "columns" below can hold a list or tuple
|
||||||
# of column widths expressed as strings, that will be given as is in
|
# of column widths expressed as strings, that will be given as is in
|
||||||
|
@ -65,6 +65,7 @@ class Group:
|
||||||
# If the group is rendered into another group, we can specify the number
|
# If the group is rendered into another group, we can specify the number
|
||||||
# of columns that this group will span.
|
# of columns that this group will span.
|
||||||
self.colspan = colspan
|
self.colspan = colspan
|
||||||
|
self.valign = valign
|
||||||
if style == 'tabs':
|
if style == 'tabs':
|
||||||
# Group content will be rendered as tabs. In this case, some
|
# Group content will be rendered as tabs. In this case, some
|
||||||
# param combinations have no sense.
|
# param combinations have no sense.
|
||||||
|
@ -987,7 +988,8 @@ class Date(Type):
|
||||||
hourParts = ('hour', 'minute')
|
hourParts = ('hour', 'minute')
|
||||||
def __init__(self, validator=None, multiplicity=(0,1), index=None,
|
def __init__(self, validator=None, multiplicity=(0,1), index=None,
|
||||||
default=None, optional=False, editDefault=False,
|
default=None, optional=False, editDefault=False,
|
||||||
format=WITH_HOUR, startYear=time.localtime()[0]-10,
|
format=WITH_HOUR, calendar=True,
|
||||||
|
startYear=time.localtime()[0]-10,
|
||||||
endYear=time.localtime()[0]+10, show=True, page='main',
|
endYear=time.localtime()[0]+10, show=True, page='main',
|
||||||
group=None, layouts=None, move=0, indexed=False,
|
group=None, layouts=None, move=0, indexed=False,
|
||||||
searchable=False, specificReadPermission=False,
|
searchable=False, specificReadPermission=False,
|
||||||
|
@ -995,6 +997,7 @@ class Date(Type):
|
||||||
colspan=1, master=None, masterValue=None, focus=False,
|
colspan=1, master=None, masterValue=None, focus=False,
|
||||||
historized=False):
|
historized=False):
|
||||||
self.format = format
|
self.format = format
|
||||||
|
self.calendar = calendar
|
||||||
self.startYear = startYear
|
self.startYear = startYear
|
||||||
self.endYear = endYear
|
self.endYear = endYear
|
||||||
Type.__init__(self, validator, multiplicity, index, default, optional,
|
Type.__init__(self, validator, multiplicity, index, default, optional,
|
||||||
|
@ -1164,12 +1167,14 @@ class Ref(Type):
|
||||||
def getDefaultLayouts(self): return {'view': 'l-f', 'edit': 'lrv-f'}
|
def getDefaultLayouts(self): return {'view': 'l-f', 'edit': 'lrv-f'}
|
||||||
|
|
||||||
def isShowable(self, obj, layoutType):
|
def isShowable(self, obj, layoutType):
|
||||||
|
res = Type.isShowable(self, obj, layout)
|
||||||
|
if not res: return res
|
||||||
if (layoutType == 'edit') and self.add: return False
|
if (layoutType == 'edit') and self.add: return False
|
||||||
if self.isBack:
|
if self.isBack:
|
||||||
if layoutType == 'edit': return False
|
if layoutType == 'edit': return False
|
||||||
else:
|
else:
|
||||||
return obj.getBRefs(self.relationship)
|
return obj.getBRefs(self.relationship)
|
||||||
return Type.isShowable(self, obj, layout)
|
return True
|
||||||
|
|
||||||
def getValue(self, obj):
|
def getValue(self, obj):
|
||||||
if self.isBack:
|
if self.isBack:
|
||||||
|
|
|
@ -148,6 +148,9 @@ class Generator(AbstractGenerator):
|
||||||
msg('file_required', '', msg.FILE_REQUIRED),
|
msg('file_required', '', msg.FILE_REQUIRED),
|
||||||
msg('image_required', '', msg.IMAGE_REQUIRED),
|
msg('image_required', '', msg.IMAGE_REQUIRED),
|
||||||
]
|
]
|
||||||
|
# Create a label for every role added by this application
|
||||||
|
for role in self.getAllUsedRoles(appOnly=True):
|
||||||
|
self.labels.append(msg('role_%s' % role,'', role, niceDefault=True))
|
||||||
# Create basic files (config.py, Install.py, etc)
|
# Create basic files (config.py, Install.py, etc)
|
||||||
self.generateTool()
|
self.generateTool()
|
||||||
self.generateConfig()
|
self.generateConfig()
|
||||||
|
@ -224,7 +227,7 @@ class Generator(AbstractGenerator):
|
||||||
if not poFile.generated:
|
if not poFile.generated:
|
||||||
poFile.generate()
|
poFile.generate()
|
||||||
|
|
||||||
ploneRoles = ('Manager', 'Member', 'Owner', 'Reviewer')
|
ploneRoles = ('Manager', 'Member', 'Owner', 'Reviewer', 'Anonymous')
|
||||||
def getAllUsedRoles(self, appOnly=False):
|
def getAllUsedRoles(self, appOnly=False):
|
||||||
'''Produces a list of all the roles used within all workflows and
|
'''Produces a list of all the roles used within all workflows and
|
||||||
classes defined in this application. If p_appOnly is True, it
|
classes defined in this application. If p_appOnly is True, it
|
||||||
|
|
|
@ -133,7 +133,6 @@ class PloneInstaller:
|
||||||
if not hasattr(site.portal_types, self.appyFolderType):
|
if not hasattr(site.portal_types, self.appyFolderType):
|
||||||
self.registerAppyFolderType()
|
self.registerAppyFolderType()
|
||||||
# Create the folder
|
# Create the folder
|
||||||
|
|
||||||
if not hasattr(site.aq_base, self.productName):
|
if not hasattr(site.aq_base, self.productName):
|
||||||
# Temporarily allow me to create Appy large plone folders
|
# Temporarily allow me to create Appy large plone folders
|
||||||
getattr(site.portal_types, self.appyFolderType).global_allow = 1
|
getattr(site.portal_types, self.appyFolderType).global_allow = 1
|
||||||
|
@ -144,27 +143,29 @@ class PloneInstaller:
|
||||||
title=self.productName)
|
title=self.productName)
|
||||||
getattr(site.portal_types, self.appyFolderType).global_allow = 0
|
getattr(site.portal_types, self.appyFolderType).global_allow = 0
|
||||||
appFolder = getattr(site, self.productName)
|
appFolder = getattr(site, self.productName)
|
||||||
|
|
||||||
# All roles defined as creators should be able to create the
|
# All roles defined as creators should be able to create the
|
||||||
# corresponding root content types in this folder.
|
# corresponding root content types in this folder.
|
||||||
i = -1
|
i = -1
|
||||||
allCreators = set()
|
allCreators = set()
|
||||||
for klass in self.appClasses:
|
for klass in self.appClasses:
|
||||||
i += 1
|
i += 1
|
||||||
if klass.__dict__.has_key('root') and klass.__dict__['root']:
|
if not klass.__dict__.has_key('root') or not klass.__dict__['root']:
|
||||||
# It is a root class.
|
continue # It is not a root class
|
||||||
creators = getattr(klass, 'creators', None)
|
creators = getattr(klass, 'creators', None)
|
||||||
if not creators: creators = self.defaultAddRoles
|
if not creators: creators = self.defaultAddRoles
|
||||||
allCreators = allCreators.union(creators)
|
allCreators = allCreators.union(creators)
|
||||||
className = self.appClassNames[i]
|
className = self.appClassNames[i]
|
||||||
updateRolesForPermission(self.getAddPermission(className),
|
permission = self.getAddPermission(className)
|
||||||
tuple(creators), appFolder)
|
updateRolesForPermission(permission, tuple(creators), appFolder)
|
||||||
# Beyond content-type-specific "add" permissions, creators must also
|
# Beyond content-type-specific "add" permissions, creators must also
|
||||||
# have the main permission "Add portal content".
|
# have the main permission "Add portal content".
|
||||||
updateRolesForPermission('Add portal content', tuple(allCreators),
|
permission = 'Add portal content'
|
||||||
appFolder)
|
updateRolesForPermission(permission, tuple(allCreators), appFolder)
|
||||||
# Creates the "appy" Directory view
|
# Creates the "appy" Directory view
|
||||||
if not hasattr(site.aq_base, 'skyn'):
|
if hasattr(site.aq_base, 'skyn'):
|
||||||
|
site.manage_delObjects(['skyn'])
|
||||||
|
# This way, if Appy has moved from one place to the other, the
|
||||||
|
# directory view will always refer to the correct place.
|
||||||
addDirView = self.ploneStuff['manage_addDirectoryView']
|
addDirView = self.ploneStuff['manage_addDirectoryView']
|
||||||
addDirView(site, appy.getPath() + '/gen/plone25/skin', id='skyn')
|
addDirView(site, appy.getPath() + '/gen/plone25/skin', id='skyn')
|
||||||
|
|
||||||
|
|
|
@ -263,39 +263,6 @@ class ToolMixin(AbstractMixin):
|
||||||
return value[:maxWidth] + '...'
|
return value[:maxWidth] + '...'
|
||||||
return value
|
return value
|
||||||
|
|
||||||
xhtmlToText = re.compile('<.*?>', re.S)
|
|
||||||
def getReferenceLabel(self, brain, appyType):
|
|
||||||
'''p_appyType is a Ref with link=True. I need to display, on an edit
|
|
||||||
view, the referenced object p_brain in the listbox that will allow
|
|
||||||
the user to choose which object(s) to link through the Ref.
|
|
||||||
According to p_appyType, the label may only be the object title,
|
|
||||||
or more if parameter appyType.shownInfo is used.'''
|
|
||||||
res = brain.Title
|
|
||||||
if 'title' in appyType['shownInfo']:
|
|
||||||
# We may place it at another place
|
|
||||||
res = ''
|
|
||||||
appyObj = brain.getObject().appy()
|
|
||||||
for fieldName in appyType['shownInfo']:
|
|
||||||
value = getattr(appyObj, fieldName)
|
|
||||||
if isinstance(value, AbstractWrapper):
|
|
||||||
value = value.title.decode('utf-8')
|
|
||||||
elif isinstance(value, basestring):
|
|
||||||
value = value.decode('utf-8')
|
|
||||||
refAppyType = appyObj.o.getAppyType(fieldName)
|
|
||||||
if refAppyType and (refAppyType.type == 'String') and \
|
|
||||||
(refAppyType.format == 2):
|
|
||||||
value = self.xhtmlToText.sub(' ', value)
|
|
||||||
else:
|
|
||||||
value = str(value)
|
|
||||||
prefix = ''
|
|
||||||
if res:
|
|
||||||
prefix = ' | '
|
|
||||||
res += prefix + value.encode('utf-8')
|
|
||||||
maxWidth = self.getListBoxesMaximumWidth()
|
|
||||||
if len(res) > maxWidth:
|
|
||||||
res = res[:maxWidth-2] + '...'
|
|
||||||
return res
|
|
||||||
|
|
||||||
translationMapping = {'portal_path': ''}
|
translationMapping = {'portal_path': ''}
|
||||||
def translateWithMapping(self, label):
|
def translateWithMapping(self, label):
|
||||||
'''Translates p_label in the application domain, with a default
|
'''Translates p_label in the application domain, with a default
|
||||||
|
|
|
@ -351,17 +351,61 @@ class AbstractMixin:
|
||||||
res.objects = res.objects[0]
|
res.objects = res.objects[0]
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def getAppyRefs(self, appyType, startNumber=None):
|
def getAppyRefs(self, name, startNumber=None):
|
||||||
'''Gets the objects linked to me through Ref p_appyType.
|
'''Gets the objects linked to me through Ref field named p_name.
|
||||||
If p_startNumber is None, this method returns all referred objects.
|
If p_startNumber is None, this method returns all referred objects.
|
||||||
If p_startNumber is a number, this method will return x objects,
|
If p_startNumber is a number, this method will return x objects,
|
||||||
starting at p_startNumber, x being appyType.maxPerPage.'''
|
starting at p_startNumber, x being appyType.maxPerPage.'''
|
||||||
if not appyType['isBack']:
|
appyType = self.getAppyType(name)
|
||||||
return self._appy_getRefs(appyType['name'], ploneObjects=True,
|
if not appyType.isBack:
|
||||||
|
return self._appy_getRefs(name, ploneObjects=True,
|
||||||
startNumber=startNumber).__dict__
|
startNumber=startNumber).__dict__
|
||||||
else:
|
else:
|
||||||
# Note Pagination is not yet implemented for backward refs.
|
# Note that pagination is not yet implemented for backward refs.
|
||||||
return SomeObjects(self.getBRefs(appyType['relationship'])).__dict__
|
return SomeObjects(self.getBRefs(appyType.relationship)).__dict__
|
||||||
|
|
||||||
|
def getSelectableAppyRefs(self, name):
|
||||||
|
'''p_name is the name of a Ref field. This method returns the list of
|
||||||
|
all objects that can be selected to be linked as references to p_self
|
||||||
|
through field p_name.'''
|
||||||
|
appyType = self.getAppyType(name)
|
||||||
|
if not appyType.select:
|
||||||
|
# No select method has been defined: we must retrieve all objects
|
||||||
|
# of the referred type that the user is allowed to access.
|
||||||
|
return self.appy().search(appyType.klass)
|
||||||
|
else:
|
||||||
|
return appyType.select(self.appy())
|
||||||
|
|
||||||
|
xhtmlToText = re.compile('<.*?>', re.S)
|
||||||
|
def getReferenceLabel(self, name, refObject):
|
||||||
|
'''p_name is the name of a Ref field with link=True. I need to display,
|
||||||
|
on an edit view, the p_refObject in the listbox that will allow
|
||||||
|
the user to choose which object(s) to link through the Ref.
|
||||||
|
The information to display may only be the object title or more if
|
||||||
|
field.shownInfo is used.'''
|
||||||
|
appyType = self.getAppyType(name)
|
||||||
|
res = refObject.title
|
||||||
|
if 'title' in appyType.shownInfo:
|
||||||
|
# We may place it at another place
|
||||||
|
res = ''
|
||||||
|
for fieldName in appyType.shownInfo:
|
||||||
|
refType = refObject.o.getAppyType(fieldName)
|
||||||
|
value = getattr(refObject, fieldName)
|
||||||
|
value = refType.getFormattedValue(refObject.o, value)
|
||||||
|
if (refType.type == 'String') and (refType.format == 2):
|
||||||
|
value = self.xhtmlToText.sub(' ', value)
|
||||||
|
prefix = ''
|
||||||
|
if res:
|
||||||
|
prefix = ' | '
|
||||||
|
res += prefix + value
|
||||||
|
maxWidth = appyType.width or 30
|
||||||
|
if len(res) > maxWidth:
|
||||||
|
res = res[:maxWidth-2] + '...'
|
||||||
|
return res
|
||||||
|
|
||||||
|
def getReferenceUid(self, refObject):
|
||||||
|
'''Returns the UID of referred object p_refObject.'''
|
||||||
|
return refObject.o.UID()
|
||||||
|
|
||||||
def getAppyRefIndex(self, fieldName, obj):
|
def getAppyRefIndex(self, fieldName, obj):
|
||||||
'''Gets the position of p_obj within Ref field named p_fieldName.'''
|
'''Gets the position of p_obj within Ref field named p_fieldName.'''
|
||||||
|
@ -726,20 +770,9 @@ class AbstractMixin:
|
||||||
self.reindexObject()
|
self.reindexObject()
|
||||||
return self.goto(urlBack)
|
return self.goto(urlBack)
|
||||||
|
|
||||||
def callAppySelect(self, selectMethod, brains):
|
def getFlavour(self):
|
||||||
'''Selects objects from a Reference field.'''
|
'''Returns the flavour corresponding to this object.'''
|
||||||
if selectMethod:
|
return self.getTool().getFlavour(self.portal_type)
|
||||||
obj = self.appy()
|
|
||||||
allObjects = [b.getObject().appy() for b in brains]
|
|
||||||
filteredObjects = selectMethod(obj, allObjects)
|
|
||||||
filteredUids = [o.o.UID() for o in filteredObjects]
|
|
||||||
res = []
|
|
||||||
for b in brains:
|
|
||||||
if b.UID in filteredUids:
|
|
||||||
res.append(b)
|
|
||||||
else:
|
|
||||||
res = brains
|
|
||||||
return res
|
|
||||||
|
|
||||||
def fieldValueSelected(self, fieldName, vocabValue, dbValue):
|
def fieldValueSelected(self, fieldName, vocabValue, dbValue):
|
||||||
'''When displaying a selection box (ie a String with a validator being a
|
'''When displaying a selection box (ie a String with a validator being a
|
||||||
|
@ -888,31 +921,38 @@ class AbstractMixin:
|
||||||
folder = self.getParentNode()
|
folder = self.getParentNode()
|
||||||
# On this folder, set "add" permissions for every content type that will
|
# On this folder, set "add" permissions for every content type that will
|
||||||
# be created through reference fields
|
# be created through reference fields
|
||||||
allCreators = set()
|
allCreators = {} # One key for every add permission
|
||||||
|
addPermissions = self.getProductConfig().ADD_CONTENT_PERMISSIONS
|
||||||
for appyType in self.getAllAppyTypes():
|
for appyType in self.getAllAppyTypes():
|
||||||
if appyType.type == 'Ref':
|
if appyType.type != 'Ref': continue
|
||||||
|
if appyType.isBack or appyType.link: continue
|
||||||
|
# Indeed, no possibility to create objects with such Ref
|
||||||
refContentTypeName = self.getAppyRefPortalType(appyType.name)
|
refContentTypeName = self.getAppyRefPortalType(appyType.name)
|
||||||
refContentType = getattr(self.portal_types, refContentTypeName)
|
refContentType = getattr(self.portal_types, refContentTypeName)
|
||||||
refMetaType = refContentType.content_meta_type
|
refMetaType = refContentType.content_meta_type
|
||||||
if refMetaType in self.getProductConfig(\
|
if refMetaType not in addPermissions: continue
|
||||||
).ADD_CONTENT_PERMISSIONS:
|
# Indeed, there is no specific "add" permission is defined for tool
|
||||||
# No specific "add" permission is defined for tool and
|
# and flavour, for example.
|
||||||
# flavour, for example.
|
|
||||||
appyClass = refContentType.wrapperClass.__bases__[-1]
|
appyClass = refContentType.wrapperClass.__bases__[-1]
|
||||||
# Get roles that may add this content type
|
# Get roles that may add this content type
|
||||||
creators = getattr(appyClass, 'creators', None)
|
creators = getattr(appyClass, 'creators', None)
|
||||||
if not creators:
|
if not creators:
|
||||||
creators = self.getProductConfig().defaultAddRoles
|
creators = self.getProductConfig().defaultAddRoles
|
||||||
allCreators = allCreators.union(creators)
|
# Add those creators to the list of creators for this meta_type
|
||||||
# Grant this "add" permission to those roles
|
addPermission = addPermissions[refMetaType]
|
||||||
updateRolesForPermission(
|
if addPermission in allCreators:
|
||||||
self.getProductConfig().ADD_CONTENT_PERMISSIONS[\
|
allCreators[addPermission] = allCreators[\
|
||||||
refMetaType], creators, folder)
|
addPermission].union(creators)
|
||||||
|
else:
|
||||||
|
allCreators[addPermission] = set(creators)
|
||||||
|
# Update the permissions
|
||||||
|
for permission, creators in allCreators.iteritems():
|
||||||
|
updateRolesForPermission(permission, tuple(creators), folder)
|
||||||
# Beyond content-type-specific "add" permissions, creators must also
|
# Beyond content-type-specific "add" permissions, creators must also
|
||||||
# have the main permission "Add portal content".
|
# have the main permission "Add portal content".
|
||||||
if allCreators:
|
permission = 'Add portal content'
|
||||||
updateRolesForPermission('Add portal content', tuple(allCreators),
|
for creators in allCreators.itervalues():
|
||||||
folder)
|
updateRolesForPermission(permission, tuple(creators), folder)
|
||||||
|
|
||||||
def _appy_getPortalType(self, request):
|
def _appy_getPortalType(self, request):
|
||||||
'''Guess the portal_type of p_self from info about p_self and
|
'''Guess the portal_type of p_self from info about p_self and
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
tool python: portal.get('portal_%s' % appName.lower());
|
tool python: portal.get('portal_%s' % appName.lower());
|
||||||
contentType python:context.REQUEST.get('type_name');
|
contentType python:context.REQUEST.get('type_name');
|
||||||
flavour python: tool.getFlavour(contentType);
|
flavour python: tool.getFlavour(contentType);
|
||||||
flavourNumber python:int(context.REQUEST.get('flavourNumber'));
|
flavourNumber python:int(context.REQUEST.get('flavourNumber', 1));
|
||||||
searchName python:context.REQUEST.get('search', '')">
|
searchName python:context.REQUEST.get('search', '')">
|
||||||
|
|
||||||
<div metal:use-macro="here/skyn/page/macros/prologue"/>
|
<div metal:use-macro="here/skyn/page/macros/prologue"/>
|
||||||
|
|
|
@ -42,8 +42,9 @@
|
||||||
selected python:contextObj.dateValueSelected(name, 'year', year, rawValue)"
|
selected python:contextObj.dateValueSelected(name, 'year', year, rawValue)"
|
||||||
tal:content="year"></option>
|
tal:content="year"></option>
|
||||||
</select>
|
</select>
|
||||||
<tal:comment replace="nothing">The icon for displaying the date chooser</tal:comment>
|
<tal:comment replace="nothing">The icon for displaying the calendar (=date chooser)</tal:comment>
|
||||||
<a tal:attributes="onclick python: 'return showJsCalendar(\'%s_month\', \'%s\', \'%s_year\', \'%s_month\', \'%s_day\', null, null, %d, %d)' % (name, dummyName, name, name, name, years[0], years[-1])"><img tal:attributes="src string: $portal_url/popup_calendar.gif"/></a>
|
<a tal:condition="widget/calendar"
|
||||||
|
tal:attributes="onclick python: 'return showJsCalendar(\'%s_month\', \'%s\', \'%s_year\', \'%s_month\', \'%s_day\', null, null, %d, %d)' % (name, dummyName, name, name, name, years[0], years[-1])"><img tal:attributes="src string: $portal_url/popup_calendar.gif"/></a>
|
||||||
|
|
||||||
<tal:hour condition="python: widget['format'] == 0">
|
<tal:hour condition="python: widget['format'] == 0">
|
||||||
<select tal:define="hours python:range(0,24);"
|
<select tal:define="hours python:range(0,24);"
|
||||||
|
|
|
@ -9,9 +9,10 @@
|
||||||
on a forward reference, the "nav" parameter is added to the URL for allowing to navigate
|
on a forward reference, the "nav" parameter is added to the URL for allowing to navigate
|
||||||
from one object to the next/previous on skyn/view.</tal:comment>
|
from one object to the next/previous on skyn/view.</tal:comment>
|
||||||
<a tal:define="viewUrl obj/getUrl;
|
<a tal:define="viewUrl obj/getUrl;
|
||||||
|
includeShownInfo includeShownInfo | python:False;
|
||||||
navInfo python:'nav=ref.%s.%s.%d.%d' % (contextObj.UID(), fieldName, repeat['obj'].number()+startNumber, totalNumber);
|
navInfo python:'nav=ref.%s.%s.%d.%d' % (contextObj.UID(), fieldName, repeat['obj'].number()+startNumber, totalNumber);
|
||||||
fullUrl python: test(appyType['isBack'], viewUrl + '/?page=%s' % appyType['page'], viewUrl + '/?' + navInfo)"
|
fullUrl python: appyType['isBack'] and (viewUrl + '/?page=%s' % appyType['backd']['page']) or (viewUrl + '/?' + navInfo)"
|
||||||
tal:attributes="href fullUrl" tal:content="obj/Title"></a>
|
tal:attributes="href fullUrl" tal:content="python: (not includeShownInfo) and obj.Title() or contextObj.getReferenceLabel(fieldName, obj.appy())"></a>
|
||||||
</metal:objectTitle>
|
</metal:objectTitle>
|
||||||
|
|
||||||
<metal:objectActions define-macro="objectActions">
|
<metal:objectActions define-macro="objectActions">
|
||||||
|
@ -99,7 +100,7 @@
|
||||||
ajaxHookId python: contextObj.UID()+fieldName;
|
ajaxHookId python: contextObj.UID()+fieldName;
|
||||||
startNumber python: int(request.get('%s_startNumber' % ajaxHookId, 0));
|
startNumber python: int(request.get('%s_startNumber' % ajaxHookId, 0));
|
||||||
tool contextObj/getTool;
|
tool contextObj/getTool;
|
||||||
refObjects python:contextObj.getAppyRefs(appyType, startNumber);
|
refObjects python:contextObj.getAppyRefs(fieldName, startNumber);
|
||||||
objs refObjects/objects;
|
objs refObjects/objects;
|
||||||
totalNumber refObjects/totalNumber;
|
totalNumber refObjects/totalNumber;
|
||||||
batchSize refObjects/batchSize;
|
batchSize refObjects/batchSize;
|
||||||
|
@ -126,7 +127,8 @@
|
||||||
<tal:comment replace="nothing">Display a simplified widget if maximum number of
|
<tal:comment replace="nothing">Display a simplified widget if maximum number of
|
||||||
referenced objects is 1.</tal:comment>
|
referenced objects is 1.</tal:comment>
|
||||||
<table class="no-style-table" cellpadding="0" cellspacing="0"><tr valign="top">
|
<table class="no-style-table" cellpadding="0" cellspacing="0"><tr valign="top">
|
||||||
<td><span class="appyLabel" tal:condition="not: innerRef" tal:content="structure label"></span></td>
|
<td><span class="appyLabel" tal:condition="python: not innerRef and not appyType['link']"
|
||||||
|
tal:content="structure label"></span></td>
|
||||||
|
|
||||||
<tal:comment replace="nothing">If there is no object...</tal:comment>
|
<tal:comment replace="nothing">If there is no object...</tal:comment>
|
||||||
<tal:noObject condition="not:objs">
|
<tal:noObject condition="not:objs">
|
||||||
|
@ -135,9 +137,9 @@
|
||||||
</tal:noObject>
|
</tal:noObject>
|
||||||
|
|
||||||
<tal:comment replace="nothing">If there is an object...</tal:comment>
|
<tal:comment replace="nothing">If there is an object...</tal:comment>
|
||||||
<tal:objectIsPresent condition="python: len(objs) == 1">
|
<tal:objectIsPresent condition="objs">
|
||||||
<tal:obj repeat="obj objs">
|
<tal:obj repeat="obj objs">
|
||||||
<td><metal:showObjectTitle use-macro="portal/skyn/widgets/ref/macros/objectTitle" /></td>
|
<td tal:define="includeShownInfo python:True"><metal:showObjectTitle use-macro="portal/skyn/widgets/ref/macros/objectTitle" /></td>
|
||||||
<td tal:condition="not: appyType/isBack">
|
<td tal:condition="not: appyType/isBack">
|
||||||
<metal:showObjectActions use-macro="portal/skyn/widgets/ref/macros/objectActions" />
|
<metal:showObjectActions use-macro="portal/skyn/widgets/ref/macros/objectActions" />
|
||||||
</td>
|
</td>
|
||||||
|
@ -240,31 +242,25 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<tal:comment replace="nothing">Edit macro for an Ref.</tal:comment>
|
<tal:comment replace="nothing">Edit macro for an Ref.</tal:comment>
|
||||||
<div define-macro="edit"
|
<div metal:define-macro="edit"
|
||||||
tal:condition="widget/link"
|
tal:condition="widget/link"
|
||||||
tal:define="refPortalType python: contextObj.getAppyRefPortalType(name);
|
tal:define="rname python: 'appy_ref_%s' % name;
|
||||||
allBrains python:here.uid_catalog(portal_type=refPortalType);
|
requestValue python: request.get(rname, []);
|
||||||
brains python:contextObj.callAppySelect(widget['select'], allBrains);
|
inRequest python: request.has_key(rname);
|
||||||
|
allObjects python: contextObj.getSelectableAppyRefs(name);
|
||||||
refUids python: [o.UID() for o in here.getAppyRefs(name)['objects']];
|
refUids python: [o.UID() for o in here.getAppyRefs(name)['objects']];
|
||||||
isMultiple python:test(widget['multiplicity'][1]!=1, 'multiple', '');
|
isBeingCreated python: contextObj.isTemporary() or ('/portal_factory/' in contextObj.absolute_url())">
|
||||||
appyFieldName python: 'appy_ref_%s' % name;
|
|
||||||
inError python:test(errors.has_key(name), True, False);
|
|
||||||
isBeingCreated python: contextObj.isTemporary() or ('/portal_factory/' in contextObj.absolute_url())"
|
|
||||||
tal:attributes="class python:'appyRefEdit field' + test(inError, ' error', '')">
|
|
||||||
|
|
||||||
<tal:comment replace="nothing">This macro displays the Reference widget on an "edit" page</tal:comment>
|
<select tal:attributes="name rname;
|
||||||
|
multiple python: isMultiple and 'multiple' or ''">
|
||||||
<label tal:attributes="for python:appyFieldName" tal:content="label"></label>
|
<option tal:condition="not: isMultiple" i18n:translate="choose_a_value"></option>
|
||||||
<span class="fieldRequired" tal:condition="python: appyType['multiplicity'][0]>0"></span><br/>
|
<tal:ref repeat="refObj allObjects">
|
||||||
<div tal:condition="inError" tal:content="python: errors[field.getName()]"></div>
|
<option tal:define="uid python: contextObj.getReferenceUid(refObj)"
|
||||||
<select tal:define="valueIsInReq python:test(request.get(appyFieldName, None) != None, True, False)"
|
tal:content="python: contextObj.getReferenceLabel(name, refObj)"
|
||||||
tal:attributes="name python:'appy_ref_%s' % field.getName();
|
tal:attributes="value uid;
|
||||||
multiple isMultiple">
|
selected python:(inRequest and (uid in requestValue) or (not inRequest and ((uid in refUids)))) and True or False">
|
||||||
<option tal:condition="not: isMultiple" value="" i18n:translate="choose_a_value"/>
|
</option>
|
||||||
<option tal:repeat="brain brains"
|
</tal:ref>
|
||||||
tal:content="python: tool.getReferenceLabel(brain, appyType)"
|
|
||||||
tal:attributes="value brain/UID;
|
|
||||||
selected python:test((valueIsInReq and (brain.UID in request.get(appyFieldName, []))) or (not valueIsInReq and ((brain.UID in refUids) or (isBeingCreated and (brain.UID==defaultValueUID)))), True, False)"/>
|
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -59,7 +59,9 @@
|
||||||
requestValue python: request.get(name, None);
|
requestValue python: request.get(name, None);
|
||||||
inRequest python: request.has_key(name);
|
inRequest python: request.has_key(name);
|
||||||
errors errors | python: ();
|
errors errors | python: ();
|
||||||
inError python: test(widget['name'] in errors, True, False)">
|
inError python: (widget['name'] in errors) and True or False;
|
||||||
|
isMultiple python: (widget['multiplicity'][1] == None) or (widget['multiplicity'][1] > 1)">
|
||||||
|
|
||||||
<metal:layout use-macro="here/skyn/widgets/show/macros/layout"/>
|
<metal:layout use-macro="here/skyn/widgets/show/macros/layout"/>
|
||||||
</metal:field>
|
</metal:field>
|
||||||
|
|
||||||
|
@ -89,7 +91,7 @@
|
||||||
<tal:asTabs condition="python: widget['style'] == 'tabs'">
|
<tal:asTabs condition="python: widget['style'] == 'tabs'">
|
||||||
<table cellpadding="0" cellspacing="0" tal:attributes="width python: test(widget['wide'], '100%', '')">
|
<table cellpadding="0" cellspacing="0" tal:attributes="width python: test(widget['wide'], '100%', '')">
|
||||||
<tal:comment replace="nothing">First row: the tabs.</tal:comment>
|
<tal:comment replace="nothing">First row: the tabs.</tal:comment>
|
||||||
<tr><td style="border-bottom: 1px solid #ff8040">
|
<tr valign="middle"><td style="border-bottom: 1px solid #ff8040">
|
||||||
<table cellpadding="0" cellspacing="0" style="position:relative; bottom:-1px;">
|
<table cellpadding="0" cellspacing="0" style="position:relative; bottom:-1px;">
|
||||||
<tr valign="bottom">
|
<tr valign="bottom">
|
||||||
<tal:tab repeat="widgetRow widget/widgets">
|
<tal:tab repeat="widgetRow widget/widgets">
|
||||||
|
@ -157,7 +159,7 @@
|
||||||
</th>
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
<tal:comment replace="nothing">The rows of widgets</tal:comment>
|
<tal:comment replace="nothing">The rows of widgets</tal:comment>
|
||||||
<tr valign="top" tal:repeat="widgetRow widget/widgets">
|
<tr tal:attributes="valign widget/valign" tal:repeat="widgetRow widget/widgets">
|
||||||
<td tal:repeat="widget widgetRow"
|
<td tal:repeat="widget widgetRow"
|
||||||
tal:attributes="colspan widget/colspan|python:1;
|
tal:attributes="colspan widget/colspan|python:1;
|
||||||
style python: test(repeat['widget'].number() != len(widgetRow), 'padding-right: 0.6em', '')">
|
style python: test(repeat['widget'].number() != len(widgetRow), 'padding-right: 0.6em', '')">
|
||||||
|
@ -176,7 +178,7 @@
|
||||||
<tal:comment replace="nothing">Displays a field label.</tal:comment>
|
<tal:comment replace="nothing">Displays a field label.</tal:comment>
|
||||||
<tal:label metal:define-macro="label" condition="widget/hasLabel">
|
<tal:label metal:define-macro="label" condition="widget/hasLabel">
|
||||||
<label tal:attributes="for widget/name"
|
<label tal:attributes="for widget/name"
|
||||||
tal:condition="python: widget['type'] not in ('Action', 'Ref')"
|
tal:condition="python: not ((widget['type'] == 'Action') or ((widget['type'] == 'Ref') and (widget['add'])))"
|
||||||
tal:content="structure python: contextObj.translate(widget['labelId'])"></label>
|
tal:content="structure python: contextObj.translate(widget['labelId'])"></label>
|
||||||
</tal:label>
|
</tal:label>
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,12 @@
|
||||||
<tal:comment replace="nothing">View macro for a String.</tal:comment>
|
<tal:comment replace="nothing">View macro for a String.</tal:comment>
|
||||||
<metal:view define-macro="view"
|
<metal:view define-macro="view"
|
||||||
tal:define="fmt widget/format;
|
tal:define="fmt widget/format">
|
||||||
maxMult python: widget['multiplicity'][1];
|
|
||||||
severalValues python: (maxMult == None) or (maxMult > 1)">
|
|
||||||
<span tal:condition="python: fmt in (0, 3)"
|
<span tal:condition="python: fmt in (0, 3)"
|
||||||
tal:attributes="class widget/master_css; id rawValue">
|
tal:attributes="class widget/master_css; id rawValue">
|
||||||
<ul class="appyList" tal:condition="python: value and severalValues">
|
<ul class="appyList" tal:condition="python: value and isMultiple">
|
||||||
<li class="appyBullet" tal:repeat="sv value"><i tal:content="structure sv"></i></li>
|
<li class="appyBullet" tal:repeat="sv value"><i tal:content="structure sv"></i></li>
|
||||||
</ul>
|
</ul>
|
||||||
<tal:singleValue condition="python: value and not severalValues">
|
<tal:singleValue condition="python: value and not isMultiple">
|
||||||
<span tal:condition="python: fmt != 3" tal:replace="structure value"/>
|
<span tal:condition="python: fmt != 3" tal:replace="structure value"/>
|
||||||
<span tal:condition="python: fmt == 3">********</span>
|
<span tal:condition="python: fmt == 3">********</span>
|
||||||
</tal:singleValue>
|
</tal:singleValue>
|
||||||
|
|
|
@ -14,7 +14,6 @@ label { font-weight: bold; font-style: italic; }
|
||||||
.appyNav { padding: 0.4em 0 0.4em 0; }
|
.appyNav { padding: 0.4em 0 0.4em 0; }
|
||||||
.appyFocus { color: #900101; }
|
.appyFocus { color: #900101; }
|
||||||
.appyTitle { padding-top: 0.5em; font-size: 110%; }
|
.appyTitle { padding-top: 0.5em; font-size: 110%; }
|
||||||
.appyRefEdit { line-height: 1.5em; }
|
|
||||||
.appyWorkflow { text-align: center; background-color: &dtml-globalBackgroundColor;;}
|
.appyWorkflow { text-align: center; background-color: &dtml-globalBackgroundColor;;}
|
||||||
|
|
||||||
.appyPlusImg {
|
.appyPlusImg {
|
||||||
|
|
Loading…
Reference in a new issue