Two important bugfixes: one security-related, one linked to Ref fields with link=True.
This commit is contained in:
		
							parent
							
								
									dbcadc506d
								
							
						
					
					
						commit
						fa974239f3
					
				
					 12 changed files with 149 additions and 138 deletions
				
			
		
							
								
								
									
										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,29 +143,31 @@ 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') | ||||||
| 
 | 
 | ||||||
|     def installTypes(self): |     def installTypes(self): | ||||||
|         '''Registers and configures the Plone content types that correspond to |         '''Registers and configures the Plone content types that correspond to | ||||||
|  |  | ||||||
|  | @ -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…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Gaetan Delannay
						Gaetan Delannay