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.
|
||||
As prerequisite, you must have installed Plone through the Unifier installer
|
||||
available at http://plone.org.'''
|
||||
|
|
|
@ -30,7 +30,7 @@ class Group:
|
|||
'''Used for describing a group of widgets within a page.'''
|
||||
def __init__(self, name, columns=['100%'], wide=True, style='fieldset',
|
||||
hasLabel=True, hasDescr=False, hasHelp=False,
|
||||
hasHeaders=False, group=None, colspan=1):
|
||||
hasHeaders=False, group=None, colspan=1, valign='top'):
|
||||
self.name = name
|
||||
# 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
|
||||
|
@ -65,6 +65,7 @@ class Group:
|
|||
# If the group is rendered into another group, we can specify the number
|
||||
# of columns that this group will span.
|
||||
self.colspan = colspan
|
||||
self.valign = valign
|
||||
if style == 'tabs':
|
||||
# Group content will be rendered as tabs. In this case, some
|
||||
# param combinations have no sense.
|
||||
|
@ -987,7 +988,8 @@ class Date(Type):
|
|||
hourParts = ('hour', 'minute')
|
||||
def __init__(self, validator=None, multiplicity=(0,1), index=None,
|
||||
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',
|
||||
group=None, layouts=None, move=0, indexed=False,
|
||||
searchable=False, specificReadPermission=False,
|
||||
|
@ -995,6 +997,7 @@ class Date(Type):
|
|||
colspan=1, master=None, masterValue=None, focus=False,
|
||||
historized=False):
|
||||
self.format = format
|
||||
self.calendar = calendar
|
||||
self.startYear = startYear
|
||||
self.endYear = endYear
|
||||
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 isShowable(self, obj, layoutType):
|
||||
res = Type.isShowable(self, obj, layout)
|
||||
if not res: return res
|
||||
if (layoutType == 'edit') and self.add: return False
|
||||
if self.isBack:
|
||||
if layoutType == 'edit': return False
|
||||
else:
|
||||
return obj.getBRefs(self.relationship)
|
||||
return Type.isShowable(self, obj, layout)
|
||||
return True
|
||||
|
||||
def getValue(self, obj):
|
||||
if self.isBack:
|
||||
|
|
|
@ -148,6 +148,9 @@ class Generator(AbstractGenerator):
|
|||
msg('file_required', '', msg.FILE_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)
|
||||
self.generateTool()
|
||||
self.generateConfig()
|
||||
|
@ -224,7 +227,7 @@ class Generator(AbstractGenerator):
|
|||
if not poFile.generated:
|
||||
poFile.generate()
|
||||
|
||||
ploneRoles = ('Manager', 'Member', 'Owner', 'Reviewer')
|
||||
ploneRoles = ('Manager', 'Member', 'Owner', 'Reviewer', 'Anonymous')
|
||||
def getAllUsedRoles(self, appOnly=False):
|
||||
'''Produces a list of all the roles used within all workflows and
|
||||
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):
|
||||
self.registerAppyFolderType()
|
||||
# Create the folder
|
||||
|
||||
if not hasattr(site.aq_base, self.productName):
|
||||
# Temporarily allow me to create Appy large plone folders
|
||||
getattr(site.portal_types, self.appyFolderType).global_allow = 1
|
||||
|
@ -144,29 +143,31 @@ class PloneInstaller:
|
|||
title=self.productName)
|
||||
getattr(site.portal_types, self.appyFolderType).global_allow = 0
|
||||
appFolder = getattr(site, self.productName)
|
||||
|
||||
# All roles defined as creators should be able to create the
|
||||
# corresponding root content types in this folder.
|
||||
i = -1
|
||||
allCreators = set()
|
||||
for klass in self.appClasses:
|
||||
i += 1
|
||||
if klass.__dict__.has_key('root') and klass.__dict__['root']:
|
||||
# It is a root class.
|
||||
creators = getattr(klass, 'creators', None)
|
||||
if not creators: creators = self.defaultAddRoles
|
||||
allCreators = allCreators.union(creators)
|
||||
className = self.appClassNames[i]
|
||||
updateRolesForPermission(self.getAddPermission(className),
|
||||
tuple(creators), appFolder)
|
||||
if not klass.__dict__.has_key('root') or not klass.__dict__['root']:
|
||||
continue # It is not a root class
|
||||
creators = getattr(klass, 'creators', None)
|
||||
if not creators: creators = self.defaultAddRoles
|
||||
allCreators = allCreators.union(creators)
|
||||
className = self.appClassNames[i]
|
||||
permission = self.getAddPermission(className)
|
||||
updateRolesForPermission(permission, tuple(creators), appFolder)
|
||||
# Beyond content-type-specific "add" permissions, creators must also
|
||||
# have the main permission "Add portal content".
|
||||
updateRolesForPermission('Add portal content', tuple(allCreators),
|
||||
appFolder)
|
||||
permission = 'Add portal content'
|
||||
updateRolesForPermission(permission, tuple(allCreators), appFolder)
|
||||
# Creates the "appy" Directory view
|
||||
if not hasattr(site.aq_base, 'skyn'):
|
||||
addDirView = self.ploneStuff['manage_addDirectoryView']
|
||||
addDirView(site, appy.getPath() + '/gen/plone25/skin',id='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(site, appy.getPath() + '/gen/plone25/skin', id='skyn')
|
||||
|
||||
def installTypes(self):
|
||||
'''Registers and configures the Plone content types that correspond to
|
||||
|
|
|
@ -263,39 +263,6 @@ class ToolMixin(AbstractMixin):
|
|||
return value[:maxWidth] + '...'
|
||||
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': ''}
|
||||
def translateWithMapping(self, label):
|
||||
'''Translates p_label in the application domain, with a default
|
||||
|
|
|
@ -351,17 +351,61 @@ class AbstractMixin:
|
|||
res.objects = res.objects[0]
|
||||
return res
|
||||
|
||||
def getAppyRefs(self, appyType, startNumber=None):
|
||||
'''Gets the objects linked to me through Ref p_appyType.
|
||||
def getAppyRefs(self, name, startNumber=None):
|
||||
'''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 a number, this method will return x objects,
|
||||
starting at p_startNumber, x being appyType.maxPerPage.'''
|
||||
if not appyType['isBack']:
|
||||
return self._appy_getRefs(appyType['name'], ploneObjects=True,
|
||||
startNumber=startNumber).__dict__
|
||||
appyType = self.getAppyType(name)
|
||||
if not appyType.isBack:
|
||||
return self._appy_getRefs(name, ploneObjects=True,
|
||||
startNumber=startNumber).__dict__
|
||||
else:
|
||||
# Note Pagination is not yet implemented for backward refs.
|
||||
return SomeObjects(self.getBRefs(appyType['relationship'])).__dict__
|
||||
# Note that pagination is not yet implemented for backward refs.
|
||||
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):
|
||||
'''Gets the position of p_obj within Ref field named p_fieldName.'''
|
||||
|
@ -726,20 +770,9 @@ class AbstractMixin:
|
|||
self.reindexObject()
|
||||
return self.goto(urlBack)
|
||||
|
||||
def callAppySelect(self, selectMethod, brains):
|
||||
'''Selects objects from a Reference field.'''
|
||||
if selectMethod:
|
||||
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 getFlavour(self):
|
||||
'''Returns the flavour corresponding to this object.'''
|
||||
return self.getTool().getFlavour(self.portal_type)
|
||||
|
||||
def fieldValueSelected(self, fieldName, vocabValue, dbValue):
|
||||
'''When displaying a selection box (ie a String with a validator being a
|
||||
|
@ -888,31 +921,38 @@ class AbstractMixin:
|
|||
folder = self.getParentNode()
|
||||
# On this folder, set "add" permissions for every content type that will
|
||||
# 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():
|
||||
if appyType.type == 'Ref':
|
||||
refContentTypeName = self.getAppyRefPortalType(appyType.name)
|
||||
refContentType = getattr(self.portal_types, refContentTypeName)
|
||||
refMetaType = refContentType.content_meta_type
|
||||
if refMetaType in self.getProductConfig(\
|
||||
).ADD_CONTENT_PERMISSIONS:
|
||||
# No specific "add" permission is defined for tool and
|
||||
# flavour, for example.
|
||||
appyClass = refContentType.wrapperClass.__bases__[-1]
|
||||
# Get roles that may add this content type
|
||||
creators = getattr(appyClass, 'creators', None)
|
||||
if not creators:
|
||||
creators = self.getProductConfig().defaultAddRoles
|
||||
allCreators = allCreators.union(creators)
|
||||
# Grant this "add" permission to those roles
|
||||
updateRolesForPermission(
|
||||
self.getProductConfig().ADD_CONTENT_PERMISSIONS[\
|
||||
refMetaType], creators, folder)
|
||||
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)
|
||||
refContentType = getattr(self.portal_types, refContentTypeName)
|
||||
refMetaType = refContentType.content_meta_type
|
||||
if refMetaType not in addPermissions: continue
|
||||
# Indeed, there is no specific "add" permission is defined for tool
|
||||
# and flavour, for example.
|
||||
appyClass = refContentType.wrapperClass.__bases__[-1]
|
||||
# Get roles that may add this content type
|
||||
creators = getattr(appyClass, 'creators', None)
|
||||
if not creators:
|
||||
creators = self.getProductConfig().defaultAddRoles
|
||||
# Add those creators to the list of creators for this meta_type
|
||||
addPermission = addPermissions[refMetaType]
|
||||
if addPermission in allCreators:
|
||||
allCreators[addPermission] = allCreators[\
|
||||
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
|
||||
# have the main permission "Add portal content".
|
||||
if allCreators:
|
||||
updateRolesForPermission('Add portal content', tuple(allCreators),
|
||||
folder)
|
||||
permission = 'Add portal content'
|
||||
for creators in allCreators.itervalues():
|
||||
updateRolesForPermission(permission, tuple(creators), folder)
|
||||
|
||||
def _appy_getPortalType(self, request):
|
||||
'''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());
|
||||
contentType python:context.REQUEST.get('type_name');
|
||||
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', '')">
|
||||
|
||||
<div metal:use-macro="here/skyn/page/macros/prologue"/>
|
||||
|
|
|
@ -42,8 +42,9 @@
|
|||
selected python:contextObj.dateValueSelected(name, 'year', year, rawValue)"
|
||||
tal:content="year"></option>
|
||||
</select>
|
||||
<tal:comment replace="nothing">The icon for displaying the 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>
|
||||
<tal:comment replace="nothing">The icon for displaying the calendar (=date chooser)</tal:comment>
|
||||
<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">
|
||||
<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
|
||||
from one object to the next/previous on skyn/view.</tal:comment>
|
||||
<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);
|
||||
fullUrl python: test(appyType['isBack'], viewUrl + '/?page=%s' % appyType['page'], viewUrl + '/?' + navInfo)"
|
||||
tal:attributes="href fullUrl" tal:content="obj/Title"></a>
|
||||
fullUrl python: appyType['isBack'] and (viewUrl + '/?page=%s' % appyType['backd']['page']) or (viewUrl + '/?' + navInfo)"
|
||||
tal:attributes="href fullUrl" tal:content="python: (not includeShownInfo) and obj.Title() or contextObj.getReferenceLabel(fieldName, obj.appy())"></a>
|
||||
</metal:objectTitle>
|
||||
|
||||
<metal:objectActions define-macro="objectActions">
|
||||
|
@ -99,7 +100,7 @@
|
|||
ajaxHookId python: contextObj.UID()+fieldName;
|
||||
startNumber python: int(request.get('%s_startNumber' % ajaxHookId, 0));
|
||||
tool contextObj/getTool;
|
||||
refObjects python:contextObj.getAppyRefs(appyType, startNumber);
|
||||
refObjects python:contextObj.getAppyRefs(fieldName, startNumber);
|
||||
objs refObjects/objects;
|
||||
totalNumber refObjects/totalNumber;
|
||||
batchSize refObjects/batchSize;
|
||||
|
@ -126,7 +127,8 @@
|
|||
<tal:comment replace="nothing">Display a simplified widget if maximum number of
|
||||
referenced objects is 1.</tal:comment>
|
||||
<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:noObject condition="not:objs">
|
||||
|
@ -135,9 +137,9 @@
|
|||
</tal:noObject>
|
||||
|
||||
<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">
|
||||
<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">
|
||||
<metal:showObjectActions use-macro="portal/skyn/widgets/ref/macros/objectActions" />
|
||||
</td>
|
||||
|
@ -240,31 +242,25 @@
|
|||
</div>
|
||||
|
||||
<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:define="refPortalType python: contextObj.getAppyRefPortalType(name);
|
||||
allBrains python:here.uid_catalog(portal_type=refPortalType);
|
||||
brains python:contextObj.callAppySelect(widget['select'], allBrains);
|
||||
refUids python: [o.UID() for o in here.getAppyRefs(name)['objects']];
|
||||
isMultiple python:test(widget['multiplicity'][1]!=1, 'multiple', '');
|
||||
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:define="rname python: 'appy_ref_%s' % name;
|
||||
requestValue python: request.get(rname, []);
|
||||
inRequest python: request.has_key(rname);
|
||||
allObjects python: contextObj.getSelectableAppyRefs(name);
|
||||
refUids python: [o.UID() for o in here.getAppyRefs(name)['objects']];
|
||||
isBeingCreated python: contextObj.isTemporary() or ('/portal_factory/' in contextObj.absolute_url())">
|
||||
|
||||
<tal:comment replace="nothing">This macro displays the Reference widget on an "edit" page</tal:comment>
|
||||
|
||||
<label tal:attributes="for python:appyFieldName" tal:content="label"></label>
|
||||
<span class="fieldRequired" tal:condition="python: appyType['multiplicity'][0]>0"></span><br/>
|
||||
<div tal:condition="inError" tal:content="python: errors[field.getName()]"></div>
|
||||
<select tal:define="valueIsInReq python:test(request.get(appyFieldName, None) != None, True, False)"
|
||||
tal:attributes="name python:'appy_ref_%s' % field.getName();
|
||||
multiple isMultiple">
|
||||
<option tal:condition="not: isMultiple" value="" i18n:translate="choose_a_value"/>
|
||||
<option tal:repeat="brain brains"
|
||||
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 tal:attributes="name rname;
|
||||
multiple python: isMultiple and 'multiple' or ''">
|
||||
<option tal:condition="not: isMultiple" i18n:translate="choose_a_value"></option>
|
||||
<tal:ref repeat="refObj allObjects">
|
||||
<option tal:define="uid python: contextObj.getReferenceUid(refObj)"
|
||||
tal:content="python: contextObj.getReferenceLabel(name, refObj)"
|
||||
tal:attributes="value uid;
|
||||
selected python:(inRequest and (uid in requestValue) or (not inRequest and ((uid in refUids)))) and True or False">
|
||||
</option>
|
||||
</tal:ref>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -59,7 +59,9 @@
|
|||
requestValue python: request.get(name, None);
|
||||
inRequest python: request.has_key(name);
|
||||
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:field>
|
||||
|
||||
|
@ -89,7 +91,7 @@
|
|||
<tal:asTabs condition="python: widget['style'] == 'tabs'">
|
||||
<table cellpadding="0" cellspacing="0" tal:attributes="width python: test(widget['wide'], '100%', '')">
|
||||
<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;">
|
||||
<tr valign="bottom">
|
||||
<tal:tab repeat="widgetRow widget/widgets">
|
||||
|
@ -157,7 +159,7 @@
|
|||
</th>
|
||||
</tr>
|
||||
<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"
|
||||
tal:attributes="colspan widget/colspan|python:1;
|
||||
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:label metal:define-macro="label" condition="widget/hasLabel">
|
||||
<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:label>
|
||||
|
||||
|
|
|
@ -1,14 +1,12 @@
|
|||
<tal:comment replace="nothing">View macro for a String.</tal:comment>
|
||||
<metal:view define-macro="view"
|
||||
tal:define="fmt widget/format;
|
||||
maxMult python: widget['multiplicity'][1];
|
||||
severalValues python: (maxMult == None) or (maxMult > 1)">
|
||||
tal:define="fmt widget/format">
|
||||
<span tal:condition="python: fmt in (0, 3)"
|
||||
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>
|
||||
</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">********</span>
|
||||
</tal:singleValue>
|
||||
|
|
|
@ -14,7 +14,6 @@ label { font-weight: bold; font-style: italic; }
|
|||
.appyNav { padding: 0.4em 0 0.4em 0; }
|
||||
.appyFocus { color: #900101; }
|
||||
.appyTitle { padding-top: 0.5em; font-size: 110%; }
|
||||
.appyRefEdit { line-height: 1.5em; }
|
||||
.appyWorkflow { text-align: center; background-color: &dtml-globalBackgroundColor;;}
|
||||
|
||||
.appyPlusImg {
|
||||
|
|
Loading…
Reference in a new issue