appy.gen: bugfixes in Ref fields, IE CSS and master/slave relationships.

This commit is contained in:
Gaetan Delannay 2011-10-04 20:12:58 +02:00
parent e821307b4c
commit 4b44f8d565
11 changed files with 98 additions and 87 deletions

View file

@ -23,11 +23,11 @@ emptyTuple = ()
labelTypes = ('label', 'descr', 'help') labelTypes = ('label', 'descr', 'help')
def initMasterValue(v): def initMasterValue(v):
'''Standardizes p_v as a list.''' '''Standardizes p_v as a list of strings.'''
if not v: res = [] if not v: res = []
elif type(v) not in sequenceTypes: res = [v] elif type(v) not in sequenceTypes: res = [v]
else: res = v else: res = v
return res return [str(v) for v in res]
# Descriptor classes used for refining descriptions of elements in types # Descriptor classes used for refining descriptions of elements in types
# (pages, groups,...) ---------------------------------------------------------- # (pages, groups,...) ----------------------------------------------------------
@ -1809,6 +1809,43 @@ class Ref(Type):
elif nbOfRefs > maxRef: elif nbOfRefs > maxRef:
return obj.translate('max_ref_violated') return obj.translate('max_ref_violated')
def linkObject(self, obj, value, back=False):
'''This method links p_value (which can be a list of objects) to p_obj
through this Ref field.'''
# p_value can be a list of objects
if type(value) in sequenceTypes:
for v in value: self.linkObject(obj, v, back=back)
return
# Gets the list of referred objects (=list of uids), or create it.
obj = obj.o
refs = getattr(obj, self.name, None)
if refs == None:
refs = obj.getProductConfig().PersistentList()
setattr(obj, self.name, refs)
# Insert p_value into it.
uid = value.o.UID()
if uid not in refs:
refs.append(uid)
# Update the back reference
if not back: self.back.linkObject(value, obj, back=True)
def unlinkObject(self, obj, value, back=False):
'''This method unlinks p_value (which can be a list of objects) from
p_obj through this Ref field.'''
# p_value can be a list of objects
if type(value) in sequenceTypes:
for v in value: self.unlinkObject(obj, v, back=back)
return
obj = obj.o
refs = getattr(obj, self.name, None)
if not refs: return
# Unlink p_value
uid = value.o.UID()
if uid in refs:
refs.remove(uid)
# Update the back reference
if not back: self.back.unlinkObject(value, obj, back=True)
def store(self, obj, value): def store(self, obj, value):
'''Stores on p_obj, the p_value, which can be: '''Stores on p_obj, the p_value, which can be:
* None; * None;
@ -1817,25 +1854,31 @@ class Ref(Type):
of UIDs come from Ref fields with link:True edited through the web; of UIDs come from Ref fields with link:True edited through the web;
* a Zope object; * a Zope object;
* a Appy object; * a Appy object;
* a list of Appy or Zope objects. * a list of Appy or Zope objects.'''
''' # Standardize p_value into a list of Zope objects
# Standardize p_value into a list of uids objects = value
uids = value if not objects: objects = []
if not uids: uids = [] if type(objects) not in sequenceTypes: objects = [objects]
if type(uids) not in sequenceTypes: uids = [uids] tool = obj.getTool()
for i in range(len(uids)): for i in range(len(objects)):
if not isinstance(uids[i], basestring): if isinstance(objects[i], basestring):
# Get the UID from the Zope or Appy object # We have a UID here
uids[i] = uids[i].o.UID() objects[i] = tool.getObject(objects[i])
# Update the list of referred uids. else:
# Be sure to have a Zope object
objects[i] = objects[i].o
uids = [o.UID() for o in objects]
# Unlink objects that are not referred anymore
refs = getattr(obj, self.name, None) refs = getattr(obj, self.name, None)
if refs == None: if refs:
refs = obj.getProductConfig().PersistentList(uids) i = len(refs)-1
setattr(obj, self.name, refs) while i >= 0:
else: if refs[i] not in uids:
# Empty the list and fill it with uids # Object having this UID must unlink p_obj
del refs[:] self.back.unlinkObject(tool.getObject(refs[i]), obj)
for uid in uids: refs.append(uid) i -= 1
# Link new objects
self.linkObject(obj, objects)
def clone(self, forTool=True): def clone(self, forTool=True):
'''Produces a clone of myself.''' '''Produces a clone of myself.'''

View file

@ -84,7 +84,7 @@ class BaseMixin:
for field in self.getAllAppyTypes(): for field in self.getAllAppyTypes():
if field.type != 'Ref': continue if field.type != 'Ref': continue
for obj in field.getValue(self): for obj in field.getValue(self):
obj.unlink(field.back.name, self, back=True) field.back.unlinkObject(obj, self, back=True)
# Delete the object # Delete the object
self.getParentNode().manage_delObjects([self.id]) self.getParentNode().manage_delObjects([self.id])

View file

@ -10,10 +10,12 @@ p { margin: 0;}
acronym {cursor: help;} acronym {cursor: help;}
input[type=image] { border: 0; background: none; } input[type=image] { border: 0; background: none; }
input[type=checkbox] { border: 0; background: none; cursor: pointer;} input[type=checkbox] { border: 0; background: none; cursor: pointer;}
input[type=radio] { border: 0; background: none; cursor: pointer;}
input[type=button] { border: 1px solid #cccccc; background-color: #f8f8f8; input[type=button] { border: 1px solid #cccccc; background-color: #f8f8f8;
cursor: pointer;} cursor: pointer;}
input[type=submit] { border: 1px solid #cccccc; background-color: #f8f8f8; input[type=submit] { border: 1px solid #cccccc; background-color: #f8f8f8;
cursor: pointer; } cursor: pointer; }
input[type=password] { border: 1px solid #cccccc; background-color: #f8f8f8;}
input[type=text] { border: 1px solid #cccccc; background-color: #f8f8f8; input[type=text] { border: 1px solid #cccccc; background-color: #f8f8f8;
font-family: Lucida,Helvetica,Arial,sans-serif; font-family: Lucida,Helvetica,Arial,sans-serif;
margin-bottom: 1px} margin-bottom: 1px}
@ -48,8 +50,8 @@ img {border: 0;}
.phase { border-style: dashed; border-width: thin; padding: 0 0.6em 0 1em; } .phase { border-style: dashed; border-width: thin; padding: 0 0.6em 0 1em; }
.content { padding: 14px 3px 9px 15px;} .content { padding: 14px 3px 9px 15px;}
.grey { display: none; position: absolute; left: 0px; top: 0px; .grey { display: none; position: absolute; left: 0px; top: 0px;
width:100%; height:100%; background:grey; opacity:0.5; background:grey; opacity:0.5; -moz-opacity:0.5; -khtml-opacity:0.5;
-moz-opacity:0.5; -khtml-opacity:0.5; filter:alpha(Opacity=50);} filter:alpha(Opacity=50);}
.popup { display: none; position: absolute; top: 30%; left: 35%; .popup { display: none; position: absolute; top: 30%; left: 35%;
width: 350px; z-index : 100; background: white; padding: 8px; width: 350px; z-index : 100; background: white; padding: 8px;
border: 1px solid grey; } border: 1px solid grey; }

View file

@ -248,7 +248,11 @@ function getSlaves(master) {
// Gets all the slaves of master. // Gets all the slaves of master.
allSlaves = document.getElementsByName('slave'); allSlaves = document.getElementsByName('slave');
res = []; res = [];
slavePrefix = 'slave_' + master.attributes['name'].value + '_'; masterName = master.attributes['name'].value;
if (master.type == 'checkbox') {
masterName = masterName.substr(0, masterName.length-8);
}
slavePrefix = 'slave_' + masterName + '_';
for (var i=0; i < slaves.length; i++){ for (var i=0; i < slaves.length; i++){
cssClasses = slaves[i].className.split(' '); cssClasses = slaves[i].className.split(' ');
for (var j=0; j < cssClasses.length; j++) { for (var j=0; j < cssClasses.length; j++) {
@ -381,13 +385,15 @@ function openPopup(popupId, msg) {
// Open the popup // Open the popup
var popup = document.getElementById(popupId); var popup = document.getElementById(popupId);
// Put it at the right place on the screen // Put it at the right place on the screen
var scrollTop = window.pageYOffset || document.documentElement.scrollTop || 0; var scrollTop = document.body.scrollTop || window.pageYOffset || 0;
popup.style.top = (scrollTop + 150) + 'px'; popup.style.top = (scrollTop + 150) + 'px';
popup.style.display = "block"; popup.style.display = "block";
// Show the greyed zone // Show the greyed zone
var greyed = document.getElementById('grey'); var greyed = document.getElementById('grey');
greyed.style.top = scrollTop + 'px'; greyed.style.top = scrollTop + 'px';
greyed.style.display = "block"; greyed.style.display = "block";
greyed.style.height = document.body.clientHeight;
greyed.style.width = document.body.clientWidth;
} }
function closePopup(popupId) { function closePopup(popupId) {

View file

@ -75,7 +75,7 @@
</form> </form>
<h1 tal:content="python: tool.translate('import_title')"></h1><br/> <h1 tal:content="python: tool.translate('import_title')"></h1><br/>
<table cellpadding="0" cellspacing="0" class="listing nosort" width="100%"> <table class="list" width="100%">
<tr> <tr>
<th tal:repeat="columnHeader python: importElems[0]"> <th tal:repeat="columnHeader python: importElems[0]">
<img tal:condition="python: repeat['columnHeader'].number() == 1" <img tal:condition="python: repeat['columnHeader'].number() == 1"
@ -107,7 +107,7 @@
<span tal:condition="alreadyImported" tal:replace="python: tool.translate('import_already')"/> <span tal:condition="alreadyImported" tal:replace="python: tool.translate('import_already')"/>
</td> </td>
<td align="center"> <td align="center">
<input type="checkbox" checked="checked" class="noborder" id="cbElem" name="cbElem" <input type="checkbox" checked="checked" id="cbElem" name="cbElem"
tal:attributes="value python: row[0]" tal:condition="not: alreadyImported"/> tal:attributes="value python: row[0]" tal:condition="not: alreadyImported"/>
</td> </td>
</tr> </tr>

View file

@ -7,11 +7,10 @@
<tal:comment replace="nothing">Edit macro for an Boolean.</tal:comment> <tal:comment replace="nothing">Edit macro for an Boolean.</tal:comment>
<metal:edit define-macro="edit"> <metal:edit define-macro="edit">
<input type="checkbox" <input type="checkbox"
tal:attributes="name python: name + '_visible'; tal:attributes="name python: name + '_visible'; id name;
id name;
checked python:contextObj.checkboxChecked(name, rawValue); checked python:contextObj.checkboxChecked(name, rawValue);
onClick python:'toggleCheckbox(\'%s\', \'%s_hidden\');;updateSlaves(this)' % (name, name); onClick python:'toggleCheckbox(\'%s\', \'%s_hidden\');;updateSlaves(this)' % (name, name);
class python: 'noborder %s' % masterCss"/> class masterCss"/>
<input tal:attributes="name name; <input tal:attributes="name name;
id string:${name}_hidden; id string:${name}_hidden;
value python: test(contextObj.checkboxChecked(name, rawValue), 'True', 'False')" value python: test(contextObj.checkboxChecked(name, rawValue), 'True', 'False')"
@ -28,15 +27,15 @@
tal:define="typedWidget python:'%s*bool' % widgetName"> tal:define="typedWidget python:'%s*bool' % widgetName">
<label tal:attributes="for widgetName" tal:content="python: tool.translate(widget['labelId'])"></label><br>&nbsp;&nbsp; <label tal:attributes="for widgetName" tal:content="python: tool.translate(widget['labelId'])"></label><br>&nbsp;&nbsp;
<tal:yes define="valueId python:'%s_yes' % name"> <tal:yes define="valueId python:'%s_yes' % name">
<input type="radio" class="noborder" value="True" tal:attributes="name typedWidget; id valueId"/> <input type="radio" value="True" tal:attributes="name typedWidget; id valueId"/>
<label tal:attributes="for valueId" i18n:translate="yes" i18n:domain="plone"></label> <label tal:attributes="for valueId" i18n:translate="yes" i18n:domain="plone"></label>
</tal:yes> </tal:yes>
<tal:no define="valueId python:'%s_no' % name"> <tal:no define="valueId python:'%s_no' % name">
<input type="radio" class="noborder" value="False" tal:attributes="name typedWidget; id valueId"/> <input type="radio" value="False" tal:attributes="name typedWidget; id valueId"/>
<label tal:attributes="for valueId" i18n:translate="no" i18n:domain="plone"></label> <label tal:attributes="for valueId" i18n:translate="no" i18n:domain="plone"></label>
</tal:no> </tal:no>
<tal:whatever define="valueId python:'%s_whatever' % name"> <tal:whatever define="valueId python:'%s_whatever' % name">
<input type="radio" class="noborder" value="" tal:attributes="name typedWidget; id valueId" checked="checked"/> <input type="radio" value="" tal:attributes="name typedWidget; id valueId" checked="checked"/>
<label tal:attributes="for valueId" tal:content="python: tool.translate('whatever')"></label> <label tal:attributes="for valueId" tal:content="python: tool.translate('whatever')"></label>
</tal:whatever><br/> </tal:whatever><br/>
</metal:search> </metal:search>

View file

@ -81,7 +81,7 @@
<metal:search define-macro="search" <metal:search define-macro="search"
tal:define="years python:range(widget['startYear'], widget['endYear']+1)"> tal:define="years python:range(widget['startYear'], widget['endYear']+1)">
<label tal:content="python: tool.translate(widget['labelId'])"></label> <label tal:content="python: tool.translate(widget['labelId'])"></label>
<table cellpadding="0" cellspacing="0"> <table>
<tal:comment replace="nothing">From</tal:comment> <tal:comment replace="nothing">From</tal:comment>
<tr tal:define="yearFromName python: '%s*date' % widgetName; <tr tal:define="yearFromName python: '%s*date' % widgetName;
monthFromName python: '%s_from_month' % name; monthFromName python: '%s_from_month' % name;

View file

@ -23,7 +23,7 @@
</tal:showFile> </tal:showFile>
<tal:editButtons condition="python: value and value.size"> <tal:editButtons condition="python: value and value.size">
<tal:comment replace="nothing">Keep the file untouched.</tal:comment> <tal:comment replace="nothing">Keep the file untouched.</tal:comment>
<input class="noborder" type="radio" value="nochange" <input type="radio" value="nochange"
tal:attributes="checked python:test(value.size!=0, 'checked', None); tal:attributes="checked python:test(value.size!=0, 'checked', None);
name string:${name}_delete; name string:${name}_delete;
id string:${name}_nochange; id string:${name}_nochange;
@ -33,7 +33,7 @@
<br/> <br/>
<tal:comment replace="nothing">Delete the file.</tal:comment> <tal:comment replace="nothing">Delete the file.</tal:comment>
<tal:delete condition="not: widget/required"> <tal:delete condition="not: widget/required">
<input class="noborder" type="radio" value="delete" <input type="radio" value="delete"
tal:attributes="name string:${name}_delete; tal:attributes="name string:${name}_delete;
id string:${name}_delete; id string:${name}_delete;
onclick string:document.getElementById('${name}_file').disabled=true;"/> onclick string:document.getElementById('${name}_file').disabled=true;"/>
@ -42,7 +42,7 @@
<br/> <br/>
</tal:delete> </tal:delete>
<tal:comment replace="nothing">Replace with a new file.</tal:comment> <tal:comment replace="nothing">Replace with a new file.</tal:comment>
<input class="noborder" type="radio" value="" <input type="radio" value=""
tal:attributes="checked python:test(value.size==0, 'checked', None); tal:attributes="checked python:test(value.size==0, 'checked', None);
name string:${name}_delete; name string:${name}_delete;
id string:${name}_upload; id string:${name}_upload;

View file

@ -101,7 +101,7 @@
id slaveId; name slaveId"> id slaveId; name slaveId">
<tal:comment replace="nothing">First row: the tabs.</tal:comment> <tal:comment replace="nothing">First row: the tabs.</tal:comment>
<tr valign="middle"><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 style="position:relative; bottom:-1px;">
<tr valign="middle"> <tr valign="middle">
<tal:tab repeat="widgetRow widget/widgets"> <tal:tab repeat="widgetRow widget/widgets">
<tal:id define="tabId python:'tab_%s_%d_%d' % (widget['name'], repeat['widgetRow'].number(), len(widget['widgets']))"> <tal:id define="tabId python:'tab_%s_%d_%d' % (widget['name'], repeat['widgetRow'].number(), len(widget['widgets']))">

View file

@ -90,9 +90,9 @@
orName python: '%s_or' % operName; orName python: '%s_or' % operName;
andName python: '%s_and' % operName;" andName python: '%s_and' % operName;"
condition="python: widget['multiplicity'][1]!=1"> condition="python: widget['multiplicity'][1]!=1">
<input type="radio" class="noborder" tal:attributes="name operName; id orName" checked="checked" value="or"/> <input type="radio" tal:attributes="name operName; id orName" checked="checked" value="or"/>
<label tal:attributes="for orName" tal:content="python: tool.translate('search_or')"></label> <label tal:attributes="for orName" tal:content="python: tool.translate('search_or')"></label>
<input type="radio" class="noborder" tal:attributes="name operName; id andName" value="and"/> <input type="radio" tal:attributes="name operName; id andName" value="and"/>
<label tal:attributes="for andName" tal:content="python: tool.translate('search_and')"></label><br/> <label tal:attributes="for andName" tal:content="python: tool.translate('search_and')"></label><br/>
</tal:operator> </tal:operator>
<tal:comment replace="nothing">The list of values</tal:comment> <tal:comment replace="nothing">The list of values</tal:comment>

View file

@ -86,53 +86,15 @@ class AbstractWrapper(object):
def getField(self, name): return self.o.getAppyType(name) def getField(self, name): return self.o.getAppyType(name)
def link(self, fieldName, obj, back=False): def link(self, fieldName, obj):
'''This method links p_obj (which can be a list of objects) to this one '''This method links p_obj (which can be a list of objects) to this one
through reference field p_fieldName. As this method is recursive and through reference field p_fieldName.'''
can be called to update the corresponding back reference, param return self.getField(fieldName).linkObject(self.o, obj)
p_back is there, but, you, as Appy developer, should never set it
to True.'''
# p_objs can be a list of objects
if type(obj) in sequenceTypes:
for o in obj: self.link(fieldName, o, back=back)
return
# Gets the list of referred objects (=list of uids), or create it.
selfO = self.o
refs = getattr(selfO, fieldName, None)
if refs == None:
refs = selfO.getProductConfig().PersistentList()
setattr(selfO, fieldName, refs)
# Insert p_obj into it.
uid = obj.o.UID()
if uid not in refs:
refs.append(uid)
# Update the back reference
if not back:
backName = selfO.getAppyType(fieldName).back.attribute
obj.appy().link(backName, self, back=True)
def unlink(self, fieldName, obj, back=False): def unlink(self, fieldName, obj):
'''This method unlinks p_obj (which can be a list of objects) from this '''This method unlinks p_obj (which can be a list of objects) from this
one through reference field p_fieldName. As this method is recursive one through reference field p_fieldName.'''
and can be called to update the corresponding back reference, param return self.getField(fieldName).unlinkObject(self.o, obj)
p_back is there, but, you, as Appy developer, should never set it
to True.'''
# p_objs can be a list of objects
if type(obj) in sequenceTypes:
for o in obj: self.unlink(fieldName, o, back=back)
return
# Get the list of referred objects
selfO = self.o
refs = getattr(selfO, fieldName, None)
if not refs: return
# Unlink the object
uid = obj.o.UID()
if uid in refs:
refs.remove(uid)
# Update the back reference
if not back:
backName = selfO.getAppyType(fieldName).back.attribute
obj.appy().unlink(backName, self, back=True)
def sort(self, fieldName, sortKey='title', reverse=False): def sort(self, fieldName, sortKey='title', reverse=False):
'''Sorts referred elements linked to p_self via p_fieldName according '''Sorts referred elements linked to p_self via p_fieldName according
@ -194,8 +156,7 @@ class AbstractWrapper(object):
setattr(appyObj, attrName, attrValue) setattr(appyObj, attrName, attrValue)
if isField: if isField:
# Link the object to this one # Link the object to this one
self.link(fieldName, ploneObj) appyType.linkObject(self.o, ploneObj)
self.o.reindexObject()
# Call custom initialization # Call custom initialization
if externalData: param = externalData if externalData: param = externalData
else: param = True else: param = True