[gen] Allow for ajax-based master-slave relationships within the search screen for Ref fields.
This commit is contained in:
		
							parent
							
								
									584e38abef
								
							
						
					
					
						commit
						6d6c842f12
					
				
					 16 changed files with 79 additions and 88 deletions
				
			
		|  | @ -51,15 +51,17 @@ class Field: | |||
|     pxRender = Px(''' | ||||
|      <x var="showChanges=showChanges|req.get('showChanges',False); | ||||
|              layoutType=layoutType|req.get('layoutType'); | ||||
|              isSearch = layoutType == 'search'; | ||||
|              layout=field.layouts[layoutType]; | ||||
|              name=fieldName|field.name; | ||||
|              widgetName = isSearch and ('w_%s' % name) or name; | ||||
|              sync=field.sync[layoutType]; | ||||
|              outerValue=value|None; | ||||
|              rawValue=zobj.getFieldValue(name, onlyIfSync=True, \ | ||||
|                                          layoutType=layoutType, \ | ||||
|                                          outerValue=outerValue); | ||||
|              value=zobj.getFormattedFieldValue(name, rawValue, showChanges); | ||||
|              requestValue=zobj.getRequestFieldValue(name); | ||||
|              rawValue=not isSearch and zobj.getFieldValue(name, \ | ||||
|                  onlyIfSync=True, layoutType=layoutType, outerValue=outerValue); | ||||
|              value=not isSearch and \ | ||||
|                    field.getFormattedValue(zobj, rawValue, showChanges); | ||||
|              requestValue=not isSearch and zobj.getRequestFieldValue(name); | ||||
|              inRequest=req.has_key(name); | ||||
|              error=req.get('%s_error' % name); | ||||
|              isMultiple=(field.multiplicity[1] == None) or \ | ||||
|  | @ -68,6 +70,7 @@ class Field: | |||
|              slaveCss=field.getSlaveCss(); | ||||
|              tagCss=tagCss|''; | ||||
|              tagCss=('%s %s' % (slaveCss, tagCss)).strip(); | ||||
|              zobj=zobj or ztool; | ||||
|              tagId='%s_%s' % (zobj.UID(), name); | ||||
|              tagName=field.master and 'slave' or ''; | ||||
|              layoutTarget=field">:tool.pxLayoutedObject</x>''') | ||||
|  | @ -345,8 +348,8 @@ class Field: | |||
|         '''Creates a dictionary indicating, for every layout type, if the field | ||||
|            value must be retrieved synchronously or not.''' | ||||
|         if isinstance(sync, bool): | ||||
|             sync = {'edit': sync, 'view': sync, 'cell': sync} | ||||
|         for layoutType in ('edit', 'view', 'cell'): | ||||
|             sync = {'edit': sync, 'view': sync, 'cell': sync, 'search': sync} | ||||
|         for layoutType in ('edit', 'view', 'search', 'cell'): | ||||
|             if layoutType not in sync: | ||||
|                 sync[layoutType] = False | ||||
|         return sync | ||||
|  | @ -405,9 +408,13 @@ class Field: | |||
|         for layoutType in layouts.iterkeys(): | ||||
|             if isinstance(layouts[layoutType], basestring): | ||||
|                 layouts[layoutType] = Table(layouts[layoutType]) | ||||
|         # Derive "view" and "cell" layouts from the "edit" layout when relevant | ||||
|         # Derive "view", "search" and "cell" layouts from the "edit" layout | ||||
|         # when relevant. | ||||
|         if 'view' not in layouts: | ||||
|             layouts['view'] = Table(other=layouts['edit'], derivedType='view') | ||||
|         if 'search' not in layouts: | ||||
|             layouts['search'] = Table(other=layouts['view'], | ||||
|                                       derivedType='search') | ||||
|         # Create the "cell" layout from the 'view' layout if not specified. | ||||
|         if 'cell' not in layouts: | ||||
|             layouts['cell'] = Table(other=layouts['view'], derivedType='cell') | ||||
|  | @ -587,14 +594,16 @@ class Field: | |||
|             res += '+' | ||||
|         return res | ||||
| 
 | ||||
|     def getOnChange(self, zobj, layoutType): | ||||
|     def getOnChange(self, zobj, layoutType, className=None): | ||||
|         '''When this field is a master, this method computes the call to the | ||||
|            Javascript function that will be called when its value changes (in | ||||
|            order to update slaves).''' | ||||
|         if not self.slaves: return '' | ||||
|         q = zobj.getTool().quote | ||||
|         return 'updateSlaves(this,null,%s,%s)' % \ | ||||
|                (q(zobj.absolute_url()), q(layoutType)) | ||||
|         # When the field is on a search screen, we need p_className. | ||||
|         cName = className and (',%s' % q(className)) or '' | ||||
|         return 'updateSlaves(this,null,%s,%s,null,null%s)' % \ | ||||
|                (q(zobj.absolute_url()), q(layoutType), cName) | ||||
| 
 | ||||
|     def isEmptyValue(self, value, obj=None): | ||||
|         '''Returns True if the p_value must be considered as an empty value.''' | ||||
|  |  | |||
|  | @ -38,9 +38,7 @@ class Boolean(Field): | |||
|              value=":isChecked and 'True' or 'False'"/> | ||||
|      </x>''') | ||||
| 
 | ||||
|     pxSearch = Px(''' | ||||
|      <x var="typedWidget='%s*bool' % widgetName"> | ||||
|       <label lfor=":widgetName">:_(field.labelId)</label><br/>   | ||||
|     pxSearch = Px('''<x var="typedWidget='%s*bool' % widgetName"> | ||||
|       <x var="valueId='%s_yes' % name"> | ||||
|        <input type="radio" value="True" name=":typedWidget" id=":valueId"/> | ||||
|        <label lfor=":valueId">:_('yes')</label> | ||||
|  | @ -53,8 +51,7 @@ class Boolean(Field): | |||
|        <input type="radio" value="" name=":typedWidget" id=":valueId" | ||||
|               checked="checked"/> | ||||
|        <label lfor=":valueId">:_('whatever')</label> | ||||
|       </x><br/> | ||||
|      </x>''') | ||||
|       </x><br/></x>''') | ||||
| 
 | ||||
|     def __init__(self, validator=None, multiplicity=(0,1), default=None, | ||||
|                  show=True, page='main', group=None, layouts = None, move=0, | ||||
|  |  | |||
|  | @ -38,11 +38,9 @@ class Computed(Field): | |||
|      </div> | ||||
|     </x>''') | ||||
| 
 | ||||
|     pxSearch = Px('''<x> | ||||
|      <label lfor=":name">:field.labelId</label><br/>   | ||||
|     pxSearch = Px(''' | ||||
|      <input type="text" name=":'%s*string' % name" maxlength=":field.maxChars" | ||||
|             size=":field.width" value=":field.sdefault"/> | ||||
|     </x>''') | ||||
|             size=":field.width" value=":field.sdefault"/>''') | ||||
| 
 | ||||
|     def __init__(self, validator=None, multiplicity=(0,1), default=None, | ||||
|                  show=('view', 'result'), page='main', group=None, | ||||
|  |  | |||
|  | @ -81,10 +81,7 @@ class Date(Field): | |||
|       </x> | ||||
|      </x>''') | ||||
| 
 | ||||
|     pxSearch = Px(''' | ||||
|      <x var="years=range(field.startYear, field.endYear+1)"> | ||||
|       <label>:_(field.labelId)</label> | ||||
|       <table> | ||||
|     pxSearch = Px('''<table var="years=range(field.startYear, field.endYear+1)"> | ||||
|        <!-- From --> | ||||
|        <tr var="fromName='%s_from' % name; | ||||
|                 dayFromName='%s_from_day' % name; | ||||
|  | @ -150,8 +147,7 @@ class Date(Field): | |||
|          </x> | ||||
|         </td> | ||||
|        </tr> | ||||
|       </table> | ||||
|      </x>''') | ||||
|       </table>''') | ||||
| 
 | ||||
|     # Required CSS and Javascript files for this type. | ||||
|     cssFiles = {'edit': ('jscalendar/calendar-blue.css',)} | ||||
|  |  | |||
|  | @ -35,8 +35,7 @@ class Float(Field): | |||
|             maxlength=":field.maxChars" | ||||
|             value=":inRequest and requestValue or value" type="text"/>''') | ||||
| 
 | ||||
|     pxSearch = Px('''<x> | ||||
|      <label>:_(field.labelId)</label><br/>   | ||||
|     pxSearch = Px(''' | ||||
|      <!-- From --> | ||||
|      <x var="fromName='%s*float' % widgetName"> | ||||
|       <label lfor=":fromName">:_('search_from')</label> | ||||
|  | @ -48,8 +47,7 @@ class Float(Field): | |||
|       <label lfor=":toName">:_('search_to')</label> | ||||
|       <input type="text" name=":toName" maxlength=":field.maxChars" | ||||
|              value=":field.sdefault[1]" size="field.swidth"/> | ||||
|      </x><br/> | ||||
|     </x>''') | ||||
|      </x><br/>''') | ||||
| 
 | ||||
|     def __init__(self, validator=None, multiplicity=(0,1), default=None, | ||||
|                  show=True, page='main', group=None, layouts=None, move=0, | ||||
|  |  | |||
|  | @ -32,8 +32,7 @@ class Integer(Field): | |||
|             maxlength=":field.maxChars" | ||||
|             value=":inRequest and requestValue or value" type="text"/>''') | ||||
| 
 | ||||
|     pxSearch = Px('''<x> | ||||
|      <label>:_(field.labelId)</label><br/>   | ||||
|     pxSearch = Px(''' | ||||
|      <!-- From --> | ||||
|      <x var="fromName='%s*int' % widgetName"> | ||||
|       <label lfor=":fromName">:_('search_from')</label> | ||||
|  | @ -45,8 +44,7 @@ class Integer(Field): | |||
|       <label lfor=":toName">:_('search_to')</label> | ||||
|       <input type="text" name=":toName" maxlength=":field.maxChars" | ||||
|              value=":field.sdefault[1]" size=":field.swidth"/> | ||||
|      </x><br/> | ||||
|     </x>''') | ||||
|      </x><br/>''') | ||||
| 
 | ||||
|     def __init__(self, validator=None, multiplicity=(0,1), default=None, | ||||
|                  show=True, page='main', group=None, layouts=None, move=0, | ||||
|  |  | |||
|  | @ -282,21 +282,20 @@ class Ref(Field): | |||
| 
 | ||||
|     pxEdit = Px(''' | ||||
|      <select if="field.link" | ||||
|              var2="zobjects=field.getSelectableObjects(obj); | ||||
|              var2="objects=field.getPossibleValues(obj); | ||||
|                    uids=[o.UID() for o in \ | ||||
|                          field.getLinkedObjects(zobj).objects]" | ||||
|              name=":name" id=":name" size=":isMultiple and field.height or ''" | ||||
|              onchange=":field.getOnChange(zobj, layoutType)" | ||||
|              multiple=":isMultiple"> | ||||
|       <option value="" if="not isMultiple">:_('choose_a_value')</option> | ||||
|       <option for="ztied in zobjects" var2="uid=ztied.o.UID()" | ||||
|       <option for="tied in objects" var2="uid=tied.uid" | ||||
|               selected=":inRequest and (uid in requestValue) or \ | ||||
|                                        (uid in uids)" | ||||
|               value=":uid">:field.getReferenceLabel(ztied)</option> | ||||
|               value=":uid">:field.getReferenceLabel(tied)</option> | ||||
|      </select>''') | ||||
| 
 | ||||
|     pxSearch = Px('''<x> | ||||
|      <label lfor=":widgetName">:_(field.labelId)</label><br/>   | ||||
|     pxSearch = Px(''' | ||||
|      <!-- The "and" / "or" radio buttons --> | ||||
|      <x if="field.multiplicity[1] != 1" | ||||
|         var2="operName='o_%s' % name; | ||||
|  | @ -309,12 +308,14 @@ class Ref(Field): | |||
|       <label lfor=":andName">:_('search_and')</label><br/> | ||||
|      </x> | ||||
|      <!-- The list of values --> | ||||
|      <select name=":widgetName" size=":field.sheight" multiple="multiple"> | ||||
|       <option for="v in ztool.getSearchValues(name, className)" | ||||
|               var2="uid=v[0]; title=field.getReferenceLabel(v[1])" value=":uid" | ||||
|               title=":title">:ztool.truncateValue(title,field.swidth)</option> | ||||
|      </select> | ||||
|     </x>''') | ||||
|      <select var="objects=field.getPossibleValues(tool); | ||||
|                   selectAll='masterValues' in req" | ||||
|              name=":widgetName" size=":field.sheight" multiple="multiple" | ||||
|              onchange=":field.getOnChange(ztool, 'search', className)"> | ||||
|       <option for="tied in objects" value=":tied.uid" selected=":selectAll" | ||||
|               var2="title=field.getReferenceLabel(tied, unlimited=True)" | ||||
|               title=":title">:ztool.truncateValue(title, field.swidth)</option> | ||||
|      </select>''') | ||||
| 
 | ||||
|     def __init__(self, klass=None, attribute=None, validator=None, | ||||
|                  multiplicity=(0,1), default=None, add=False, addConfirm=False, | ||||
|  | @ -740,11 +741,12 @@ class Ref(Field): | |||
|         newIndex = oldIndex + move | ||||
|         uids.insert(newIndex, uid) | ||||
| 
 | ||||
|     def getSelectableObjects(self, obj): | ||||
|         '''This method returns the list of all objects that can be selected to | ||||
|            be linked as references to p_obj via p_self. If master values are | ||||
|            present in the request, we use field.masterValues method instead of | ||||
|            self.select.''' | ||||
|     def getPossibleValues(self, obj): | ||||
|         '''This method returns the list of all objects that can be selected | ||||
|            to be linked as references to p_obj via p_self. It is applicable only | ||||
|            for ref fields with link=True. If master values are present in the | ||||
|            request, we use field.masterValues method instead of self.select. | ||||
|         ''' | ||||
|         req = obj.request | ||||
|         if 'masterValues' in req: | ||||
|             # Convert masterValue(s) from UID(s) to real object(s). | ||||
|  | @ -762,8 +764,8 @@ class Ref(Field): | |||
|         else: | ||||
|             # If this field is a ajax-updatable slave, no need to compute | ||||
|             # selectable objects: it will be overridden by method | ||||
|             # self.masterValue by a subsequent ajax request (=the "if" statement | ||||
|             # above). | ||||
|             # self.masterValue by a subsequent ajax request (=the "if" | ||||
|             # statement above). | ||||
|             if self.masterValue and callable(self.masterValue): return [] | ||||
|             if not self.select: | ||||
|                 # No select method has been defined: we must retrieve all | ||||
|  | @ -774,7 +776,7 @@ class Ref(Field): | |||
|                 return self.select(obj) | ||||
| 
 | ||||
|     xhtmlToText = re.compile('<.*?>', re.S) | ||||
|     def getReferenceLabel(self, refObject): | ||||
|     def getReferenceLabel(self, refObject, unlimited=False): | ||||
|         '''p_self must have 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 | ||||
|  | @ -793,6 +795,7 @@ class Ref(Field): | |||
|             if res: | ||||
|                 prefix = ' | ' | ||||
|             res += prefix + value | ||||
|         if unlimited: return res | ||||
|         maxWidth = self.width or 30 | ||||
|         if len(res) > maxWidth: | ||||
|             res = res[:maxWidth-2] + '...' | ||||
|  |  | |||
|  | @ -144,8 +144,7 @@ class String(Field): | |||
|       <x if="not multipleValues">:field.pxView</x> | ||||
|      </x>''') | ||||
| 
 | ||||
|     pxSearch = Px('''<x> | ||||
|      <label lfor="widgetName">:_(field.labelId)</label><br/>   | ||||
|     pxSearch = Px(''' | ||||
|      <!-- Show a simple search field for most String fields --> | ||||
|      <input if="not field.isSelect" type="text" maxlength=":field.maxChars" | ||||
|             size=":field.swidth" value=":field.sdefault" | ||||
|  | @ -173,8 +172,7 @@ class String(Field): | |||
|                selected=":v[0] in preSelected" value=":v[0]" | ||||
|                title=":v[1]">:ztool.truncateValue(v[1], field.swidth)</option> | ||||
|       </select> | ||||
|      </x><br/> | ||||
|     </x>''') | ||||
|      </x><br/>''') | ||||
| 
 | ||||
|     # Some predefined functions that may also be used as validators | ||||
|     @staticmethod | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Gaetan Delannay
						Gaetan Delannay