+
:tool.pxNavigate
- ''')
+
+
+
+ ''')
# PX that displays the list of objects the user may select to insert into a
# ref field with link="list".
pxViewPickList = Px('''
- :field.pxViewList''')
# PX that displays referred objects as menus.
@@ -269,12 +300,15 @@ class Ref(Field):
''')
- # PX that displays referred objects through this field.
+ # PX that displays referred objects through this field. In mode link="list",
+ # if, in the request, key "scope" is present and holds value "objs", the
+ # pick list (containing possible values) will not be rendered.
pxView = Px('''
- :field.pxViewPickList
+
+
+
:field.pxViewPickList
:field.pxViewList
+ var2="subLabel=linkList and 'selected_objects' or None">
+
:field.pxViewList
+ :field.pxViewList
+ :field.pxViewMenus
''')
@@ -356,10 +398,10 @@ class Ref(Field):
width=None, height=5, maxChars=None, colspan=1, master=None,
masterValue=None, focus=False, historized=False, mapping=None,
label=None, queryable=False, queryFields=None, queryNbCols=1,
- navigable=False, changeOrder=True, sdefault='', scolspan=1,
- swidth=None, sheight=None, sselect=None, persist=True,
- render='list', menuIdMethod=None, menuInfoMethod=None,
- menuUrlMethod=None):
+ navigable=False, changeOrder=True, checkboxes=True,
+ sdefault='', scolspan=1, swidth=None, sheight=None,
+ sselect=None, persist=True, render='list', menuIdMethod=None,
+ menuInfoMethod=None, menuUrlMethod=None):
self.klass = klass
self.attribute = attribute
# May the user add new objects through this ref ?
@@ -444,9 +486,13 @@ class Ref(Field):
self.queryNbCols = queryNbCols
# Within the portlet, will referred elements appear ?
self.navigable = navigable
- # If changeOrder is False, it even if the user has the right to modify
+ # If "changeOrder" is False, it even if the user has the right to modify
# the field, it will not be possible to move objects or sort them.
self.changeOrder = changeOrder
+ # If "checkboxes" is True, every linked object will be "selectable" vi
+ # a checkbox: global actions will be possible, that will act on the
+ # subset of selected objects: delete, unlink, etc.
+ self.checkboxes = checkboxes
# There are different ways to render a bunch of linked objects:
# - "list" (the default) renders them as a list (=a XHTML table);
# - "menus" renders them as a series of popup menus, grouped by type.
@@ -708,9 +754,12 @@ class Ref(Field):
elif nbOfRefs > maxRef:
return obj.translate('max_ref_violated')
- def linkObject(self, obj, value, back=False):
+ def linkObject(self, obj, value, back=False, noSecurity=True):
'''This method links p_value (which can be a list of objects) to p_obj
through this Ref field.'''
+ # Security check
+ if not noSecurity: obj.allows(self.writePermission, raiseError=True)
+
# p_value can be a list of objects
if type(value) in sutils.sequenceTypes:
for v in value: self.linkObject(obj, v, back=back)
@@ -736,9 +785,11 @@ class Ref(Field):
# Update the back reference
if not back: self.back.linkObject(value, obj, back=True)
- def unlinkObject(self, obj, value, back=False):
+ def unlinkObject(self, obj, value, back=False, noSecurity=True):
'''This method unlinks p_value (which can be a list of objects) from
p_obj through this Ref field.'''
+ # Security check
+ if not noSecurity: obj.allows(self.writePermission, raiseError=True)
# p_value can be a list of objects
if type(value) in sutils.sequenceTypes:
for v in value: self.unlinkObject(obj, v, back=back)
@@ -827,6 +878,39 @@ class Ref(Field):
else:
return self.callMethod(obj, self.changeOrder)
+ def checkboxesEnabled(self, obj):
+ '''Are checkboxes enabled?'''
+ if isinstance(self.checkboxes, bool):
+ return self.checkboxes
+ else:
+ return self.callMethod(obj, self.checkboxes)
+
+ def getCbJsInit(self, obj):
+ '''When checkboxes are enabled, this method defines a JS associative
+ array (named "_appy_objs_cbs") that will store checkboxes' statuses.
+ This array is needed because all linked objects are not visible at
+ the same time (pagination).
+
+ Moreover, if self.link is "list", an additional array (named
+ "_appy_poss_cbs") is defined for possible values.
+
+ Initial semantics of this (those) array(s) is as follows: if a key
+ is present in it for a given linked object, it means that the
+ checkbox is unchecked. All linked objects are thus selected by
+ default. This semantics may be inverted: presence of a key may mean
+ that the checkbox is checked. The current array semantics is stored
+ in a variable named "_appy_objs_sem" (or "_appy_poss_sem") and may
+ hold "unchecked" (initial semantics) or "checked" (inversed
+ semantics). Inversing semantic allows to keep the array small even
+ when checking/unchecking all checkboxes.
+
+ The mentioned JS arrays and variables are stored as attributes of the
+ DOM node representing this field.'''
+ code = "\nnode['_appy_%s_cbs']={};\nnode['_appy_%s_sem']='unchecked';"
+ poss = (self.link == 'list') and (code % ('poss', 'poss')) or ''
+ return "var node=document.getElementById('%s_%s');%s%s" % \
+ (obj.id, self.name, code % ('objs', 'objs'), poss)
+
def doChangeOrder(self, obj):
'''Moves a referred object up or down.'''
rq = obj.REQUEST
@@ -877,6 +961,37 @@ class Ref(Field):
reverse = rq.get('reverse') == 'True'
obj.appy().sort(self.name, sortKey=sortKey, reverse=reverse)
+ def onUiRequest(self, obj, rq):
+ '''This method is called when an action tied to this Ref field is
+ triggered from the user interface (link, unlink, link_many,
+ unlink_many...).'''
+ action = rq['linkAction']
+ tool = obj.getTool()
+ if action == 'link':
+ tied = tool.getObject(rq['targetUid'])
+ self.linkObject(obj, tied, noSecurity=False)
+ elif action == 'unlink':
+ tied = tool.getObject(rq['targetUid'])
+ self.unlinkObject(obj, tied, noSecurity=False)
+ elif action == 'link_many':
+ uids = rq['targetUid'].strip(',') or ();
+ if uids: uids = uids.split(',')
+ unchecked = rq['semantics'] == 'unchecked'
+ # Browse possible values
+ for tied in self.getPossibleValues(obj, removeLinked=True):
+ if unchecked:
+ # Keep only tied objects not among uids.
+ if tied.uid in uids: continue
+ else:
+ # Keep only tied objects being in uids.
+ if tied.uid not in uids: continue
+ self.linkObject(obj, tied.o, noSecurity=False)
+ elif action == 'unlink_many':
+ pass
+ urlBack = obj.getUrl(rq['HTTP_REFERER'])
+ obj.say(obj.translate('action_done'))
+ tool.goto(urlBack)
+
def autoref(klass, field):
'''klass.field is a Ref to p_klass. This kind of auto-reference can't be
declared in the "normal" way, like this:
diff --git a/gen/mixins/__init__.py b/gen/mixins/__init__.py
index da8fd2a..41d9c6f 100644
--- a/gen/mixins/__init__.py
+++ b/gen/mixins/__init__.py
@@ -179,29 +179,13 @@ class BaseMixin:
(appy.user.login, appy.klass.__name__, appy.uid))
self.goto(self.getUrl(rq['HTTP_REFERER']))
- def onUnlink(self):
- '''Called when an object unlinking is triggered from the ui.'''
- rq = self.REQUEST
- tool = self.getTool()
- sourceObject = tool.getObject(rq['sourceUid'])
- targetObject = tool.getObject(rq['targetUid'])
- field = sourceObject.getAppyType(rq['fieldName'])
- field.unlinkObject(sourceObject, targetObject)
- urlBack = self.getUrl(rq['HTTP_REFERER'])
- self.say(self.translate('action_done'))
- self.goto(urlBack)
-
def onLink(self):
- '''Called when an object linking is triggered from the ui.'''
+ '''Called when object (un)linking is triggered from the ui.'''
rq = self.REQUEST
tool = self.getTool()
sourceObject = tool.getObject(rq['sourceUid'])
- targetObject = tool.getObject(rq['targetUid'])
field = sourceObject.getAppyType(rq['fieldName'])
- field.linkObject(sourceObject, targetObject)
- urlBack = self.getUrl(rq['HTTP_REFERER'])
- self.say(self.translate('action_done'))
- self.goto(urlBack)
+ return field.onUiRequest(sourceObject, rq)
def onCreate(self):
'''This method is called when a user wants to create a root object in
diff --git a/gen/tr/Appy.pot b/gen/tr/Appy.pot
index 4e93e22..a995a9e 100644
--- a/gen/tr/Appy.pot
+++ b/gen/tr/Appy.pot
@@ -83,11 +83,11 @@ msgstr ""
msgid "add_ref"
msgstr ""
-#. Default: "Selectable elements"
+#. Default: "Available elements"
msgid "selectable_objects"
msgstr ""
-#. Default: "Selected elements"
+#. Default: "Inserted elements"
msgid "selected_objects"
msgstr ""
@@ -191,7 +191,7 @@ msgstr ""
msgid "object_delete"
msgstr ""
-#. Default: "Unlink"
+#. Default: "Remove"
msgid "object_unlink"
msgstr ""
@@ -199,6 +199,14 @@ msgstr ""
msgid "object_link"
msgstr ""
+#. Default: "Insert selected elements"
+msgid "object_link_many"
+msgstr ""
+
+#. Default: "(Un)check everything"
+msgid "check_uncheck"
+msgstr ""
+
#. Default: "Unlock"
msgid "page_unlock"
msgstr ""
diff --git a/gen/tr/ar.po b/gen/tr/ar.po
index 59190b8..2311055 100644
--- a/gen/tr/ar.po
+++ b/gen/tr/ar.po
@@ -83,11 +83,11 @@ msgstr ""
msgid "add_ref"
msgstr ""
-#. Default: "Selectable elements"
+#. Default: "Available elements"
msgid "selectable_objects"
msgstr ""
-#. Default: "Selected elements"
+#. Default: "Inserted elements"
msgid "selected_objects"
msgstr ""
@@ -191,7 +191,7 @@ msgstr "تعديل"
msgid "object_delete"
msgstr ""
-#. Default: "Unlink"
+#. Default: "Remove"
msgid "object_unlink"
msgstr ""
@@ -199,6 +199,14 @@ msgstr ""
msgid "object_link"
msgstr ""
+#. Default: "Insert selected elements"
+msgid "object_link_many"
+msgstr ""
+
+#. Default: "(Un)check everything"
+msgid "check_uncheck"
+msgstr ""
+
#. Default: "Unlock"
msgid "page_unlock"
msgstr ""
diff --git a/gen/tr/de.po b/gen/tr/de.po
index 677a7bd..9cb4317 100644
--- a/gen/tr/de.po
+++ b/gen/tr/de.po
@@ -83,11 +83,11 @@ msgstr "Kein Element"
msgid "add_ref"
msgstr "Hinzufügen"
-#. Default: "Selectable elements"
+#. Default: "Available elements"
msgid "selectable_objects"
msgstr ""
-#. Default: "Selected elements"
+#. Default: "Inserted elements"
msgid "selected_objects"
msgstr ""
@@ -191,7 +191,7 @@ msgstr "Bearbeiten"
msgid "object_delete"
msgstr "Löschen"
-#. Default: "Unlink"
+#. Default: "Remove"
msgid "object_unlink"
msgstr ""
@@ -199,6 +199,14 @@ msgstr ""
msgid "object_link"
msgstr ""
+#. Default: "Insert selected elements"
+msgid "object_link_many"
+msgstr ""
+
+#. Default: "(Un)check everything"
+msgid "check_uncheck"
+msgstr ""
+
#. Default: "Unlock"
msgid "page_unlock"
msgstr ""
diff --git a/gen/tr/en.po b/gen/tr/en.po
index 38fdd1b..d5c6d78 100644
--- a/gen/tr/en.po
+++ b/gen/tr/en.po
@@ -84,13 +84,13 @@ msgstr "No object."
msgid "add_ref"
msgstr "Add a new one"
-#. Default: "Selectable elements"
+#. Default: "Available elements"
msgid "selectable_objects"
msgstr "Selectable elements"
-#. Default: "Selected elements"
+#. Default: "Inserted elements"
msgid "selected_objects"
-msgstr "Selected elements"
+msgstr "Inserted elements"
#. Default: "The action has been successfully executed."
msgid "action_ok"
@@ -192,7 +192,7 @@ msgstr "Edit"
msgid "object_delete"
msgstr "Delete"
-#. Default: "Unlink"
+#. Default: "Remove"
msgid "object_unlink"
msgstr "Unlink"
@@ -200,6 +200,14 @@ msgstr "Unlink"
msgid "object_link"
msgstr "Insert"
+#. Default: "Insert selected elements"
+msgid "object_link_many"
+msgstr "Insert selected elements"
+
+#. Default: "(Un)check everything"
+msgid "check_uncheck"
+msgstr "(Un)check everything"
+
#. Default: "Unlock"
msgid "page_unlock"
msgstr "Unlock"
diff --git a/gen/tr/es.po b/gen/tr/es.po
index 4438e1d..c4af84e 100644
--- a/gen/tr/es.po
+++ b/gen/tr/es.po
@@ -83,11 +83,11 @@ msgstr "Ningún elemento."
msgid "add_ref"
msgstr "Añadir"
-#. Default: "Selectable elements"
+#. Default: "Available elements"
msgid "selectable_objects"
msgstr ""
-#. Default: "Selected elements"
+#. Default: "Inserted elements"
msgid "selected_objects"
msgstr ""
@@ -191,7 +191,7 @@ msgstr "Editar"
msgid "object_delete"
msgstr "Eliminar"
-#. Default: "Unlink"
+#. Default: "Remove"
msgid "object_unlink"
msgstr ""
@@ -199,6 +199,14 @@ msgstr ""
msgid "object_link"
msgstr ""
+#. Default: "Insert selected elements"
+msgid "object_link_many"
+msgstr ""
+
+#. Default: "(Un)check everything"
+msgid "check_uncheck"
+msgstr ""
+
#. Default: "Unlock"
msgid "page_unlock"
msgstr ""
diff --git a/gen/tr/fr.po b/gen/tr/fr.po
index bd2c78a..04a462b 100644
--- a/gen/tr/fr.po
+++ b/gen/tr/fr.po
@@ -84,13 +84,13 @@ msgstr "Aucun élément."
msgid "add_ref"
msgstr "Ajouter"
-#. Default: "Selectable elements"
+#. Default: "Available elements"
msgid "selectable_objects"
msgstr "Éléments sélectionnables"
-#. Default: "Selected elements"
+#. Default: "Inserted elements"
msgid "selected_objects"
-msgstr "Éléments sélectionnés"
+msgstr "Éléments insérés"
#. Default: "The action has been successfully executed."
msgid "action_ok"
@@ -192,14 +192,22 @@ msgstr "Modifier"
msgid "object_delete"
msgstr "Supprimer"
-#. Default: "Unlink"
+#. Default: "Remove"
msgid "object_unlink"
-msgstr "Dissocier"
+msgstr "Retirer"
#. Default: "Insert"
msgid "object_link"
msgstr "Insérer"
+#. Default: "Insert selected elements"
+msgid "object_link_many"
+msgstr "Insérer la sélection"
+
+#. Default: "(Un)check everything"
+msgid "check_uncheck"
+msgstr "Tout (dé)sélectionner"
+
#. Default: "Unlock"
msgid "page_unlock"
msgstr "Déverrouiller"
diff --git a/gen/tr/it.po b/gen/tr/it.po
index 924bdfb..8602bba 100644
--- a/gen/tr/it.po
+++ b/gen/tr/it.po
@@ -83,11 +83,11 @@ msgstr "Nessun elemento"
msgid "add_ref"
msgstr "Aggiungi un nuovo"
-#. Default: "Selectable elements"
+#. Default: "Available elements"
msgid "selectable_objects"
msgstr ""
-#. Default: "Selected elements"
+#. Default: "Inserted elements"
msgid "selected_objects"
msgstr ""
@@ -191,7 +191,7 @@ msgstr "Modifica"
msgid "object_delete"
msgstr "Elimina"
-#. Default: "Unlink"
+#. Default: "Remove"
msgid "object_unlink"
msgstr ""
@@ -199,6 +199,14 @@ msgstr ""
msgid "object_link"
msgstr ""
+#. Default: "Insert selected elements"
+msgid "object_link_many"
+msgstr ""
+
+#. Default: "(Un)check everything"
+msgid "check_uncheck"
+msgstr ""
+
#. Default: "Unlock"
msgid "page_unlock"
msgstr ""
diff --git a/gen/tr/nl.po b/gen/tr/nl.po
index 5b593a7..3f31f00 100644
--- a/gen/tr/nl.po
+++ b/gen/tr/nl.po
@@ -83,11 +83,11 @@ msgstr "Geen element."
msgid "add_ref"
msgstr "Toevoegen"
-#. Default: "Selectable elements"
+#. Default: "Available elements"
msgid "selectable_objects"
msgstr ""
-#. Default: "Selected elements"
+#. Default: "Inserted elements"
msgid "selected_objects"
msgstr ""
@@ -191,7 +191,7 @@ msgstr "Bewerken"
msgid "object_delete"
msgstr "Verwijderen"
-#. Default: "Unlink"
+#. Default: "Remove"
msgid "object_unlink"
msgstr ""
@@ -199,6 +199,14 @@ msgstr ""
msgid "object_link"
msgstr ""
+#. Default: "Insert selected elements"
+msgid "object_link_many"
+msgstr ""
+
+#. Default: "(Un)check everything"
+msgid "check_uncheck"
+msgstr ""
+
#. Default: "Unlock"
msgid "page_unlock"
msgstr ""
diff --git a/gen/ui/appy.css b/gen/ui/appy.css
index b0f8997..5422d28 100644
--- a/gen/ui/appy.css
+++ b/gen/ui/appy.css
@@ -66,7 +66,7 @@ img { border: 0; vertical-align: middle }
background-color: #dbdde1; font-weight: bold }
.navigate td { padding: 4px 9px }
.login { margin: 3px; color: black }
-input.button { border-width: 0 !important; color: #666666; height: 23px;
+input.button { border-width: 0 !important; color: #666666; height: 24px;
width: 150px; cursor:pointer; font-size: 90%; padding: 1px 0 0 10px;
background-color: transparent !important }
.buttons { margin-left: 4px }
@@ -157,3 +157,4 @@ td.search { padding-top: 8px }
.error { margin: 5px }
.podName { font-size: 90% }
.podTable { margin-left: 15px }
+.cbCell { width: 10px; text-align: center}
diff --git a/gen/ui/appy.js b/gen/ui/appy.js
index c179699..42f36d3 100644
--- a/gen/ui/appy.js
+++ b/gen/ui/appy.js
@@ -229,13 +229,16 @@ function askRefField(hookId, objectUrl, fieldName, innerRef, startNumber,
action, actionParams){
// Sends an Ajax request for getting the content of a reference field.
var startKey = hookId + '_startNumber';
- var params = {'innerRef': innerRef };
+ var scope = hookId.split('_').pop();
+ var params = {'innerRef': innerRef, 'scope': scope};
params[startKey] = startNumber;
if (action) params['action'] = action;
if (actionParams) {
for (key in actionParams) { params[key] = actionParams[key]; };
}
- askAjaxChunk(hookId, 'GET', objectUrl, fieldName+':pxView', params);
+ var px = (scope == 'objs')? ':pxView': ':pxViewPickList';
+ askAjaxChunk(hookId, 'GET', objectUrl, fieldName + px, params, null,
+ evalInnerScripts);
}
function askField(hookId, objectUrl, layoutType, showChanges, masterValues,
@@ -251,7 +254,7 @@ function askField(hookId, objectUrl, layoutType, showChanges, masterValues,
askAjaxChunk(hookId, 'GET', objectUrl, px, params, null, evalInnerScripts);
}
-// Function used by checkbox widgets for having radio-button-like behaviour
+// Used by checkbox widgets for having radio-button-like behaviour.
function toggleCheckbox(visibleCheckbox, hiddenBoolean) {
vis = document.getElementById(visibleCheckbox);
hidden = document.getElementById(hiddenBoolean);
@@ -259,6 +262,61 @@ function toggleCheckbox(visibleCheckbox, hiddenBoolean) {
else hidden.value = 'False';
}
+// (Un)checks a checkbox corresponding to a linked object.
+function toggleRefCb(checkbox) {
+ var name = checkbox.getAttribute('name');
+ var elems = name.split('_');
+ // Get the DOM node corresponding to the Ref field.
+ var node = document.getElementById(elems[0] + '_' + elems[1]);
+ // Get the array that stores checkbox statuses.
+ var statuses = node['_appy_' + elems[2] + '_cbs'];
+ // Get the array semantics
+ var semantics = node['_appy_' + elems[2] + '_sem'];
+ var uid = checkbox.value;
+ if (semantics == 'unchecked') {
+ if (!checkbox.checked) statuses[uid] = null;
+ else {if (uid in statuses) delete statuses[uid]};
+ }
+ else { // semantics is 'checked'
+ if (checkbox.checked) statuses[uid] = null;
+ else {if (uid in statuses) delete statuses[uid]};
+ }
+}
+
+// Initialise checkboxes of a Ref field.
+function initRefCbs(id) {
+ var elems = id.split('_');
+ // Get the DOM node corresponding to the Ref field.
+ var node = document.getElementById(elems[0] + '_' + elems[1]);
+ // Get the array that stores checkbox statuses.
+ var statuses = node['_appy_' + elems[2] + '_cbs'];
+ // Get the array semantics
+ var semantics = node['_appy_' + elems[2] + '_sem'];
+ var value = (semantics == 'unchecked')? false: true;
+ // Update visible checkboxes.
+ var checkboxes = getElementsHavingName('input', id);
+ for (var i=0; i < checkboxes.length; i++) {
+ if (checkboxes[i].value in statuses) checkboxes[i].checked = value;
+ else checkboxes[i].checked = !value;
+ }
+}
+
+// Toggle all checkboxes of a Ref field.
+function toggleAllRefCbs(id) {
+ var elems = id.split('_');
+ // Get the DOM node corresponding to the Ref field.
+ var node = document.getElementById(elems[0] + '_' + elems[1]);
+ // Empty the array that stores checkbox statuses.
+ var statuses = node['_appy_' + elems[2] + '_cbs'];
+ for (var key in statuses) delete statuses[key];
+ // Switch the array semantics.
+ var semAttr = '_appy_' + elems[2] + '_sem';
+ if (node[semAttr] == 'unchecked') node[semAttr] = 'checked';
+ else node[semAttr] = 'unchecked';
+ // Update the visible checkboxes
+ initRefCbs(id);
+}
+
// Shows/hides a dropdown menu
function toggleDropdown(dropdownId, forcedValue){
var dropdown = document.getElementById(dropdownId);
@@ -456,22 +514,35 @@ function onDeleteEvent(objectUid, eventTime) {
askConfirm('form', 'deleteEventForm', action_confirm);
}
-function onUnlinkObject(sourceUid, fieldName, targetUid) {
- f = document.getElementById('unlinkForm');
- f.sourceUid.value = sourceUid;
- f.fieldName.value = fieldName;
- f.targetUid.value = targetUid;
- askConfirm('form', 'unlinkForm', action_confirm);
-}
-
-function onLinkObject(sourceUid, fieldName, targetUid) {
+function onLink(action, sourceUid, fieldName, targetUid) {
f = document.getElementById('linkForm');
+ f.linkAction.value = action;
f.sourceUid.value = sourceUid;
f.fieldName.value = fieldName;
f.targetUid.value = targetUid;
f.submit();
}
+function onLinkMany(id) {
+ var elems = id.split('_');
+ // Get the DOM node corresponding to the Ref field.
+ var node = document.getElementById(elems[0] + '_' + elems[1]);
+ // Get the uids of (un-)checked objects.
+ var statuses = node['_appy_' + elems[2] + '_cbs'];
+ var uids = '';
+ for (var uid in statuses) uids += uid + ',';
+ // Get the array semantics
+ var semantics = node['_appy_' + elems[2] + '_sem'];
+ // Fill the form and ask for a confirmation
+ f = document.getElementById('linkForm');
+ f.linkAction.value = 'link_many';
+ f.sourceUid.value = elems[0];
+ f.fieldName.value = elems[1];
+ f.targetUid.value = uids;
+ f.semantics.value = semantics;
+ askConfirm('form', 'linkForm', action_confirm);
+}
+
function onUnlockPage(objectUid, pageName) {
f = document.getElementById('unlockForm');
f.objectUid.value = objectUid;
diff --git a/gen/ui/checkall.png b/gen/ui/checkall.png
new file mode 100644
index 0000000..e0e3c64
Binary files /dev/null and b/gen/ui/checkall.png differ
diff --git a/gen/ui/linkMany.png b/gen/ui/linkMany.png
new file mode 100644
index 0000000..e27d136
Binary files /dev/null and b/gen/ui/linkMany.png differ
diff --git a/gen/ui/unlinkUp.png b/gen/ui/unlinkUp.png
new file mode 100644
index 0000000..32fb14b
Binary files /dev/null and b/gen/ui/unlinkUp.png differ
diff --git a/gen/wrappers/ToolWrapper.py b/gen/wrappers/ToolWrapper.py
index 5a40e6d..c209dee 100644
--- a/gen/wrappers/ToolWrapper.py
+++ b/gen/wrappers/ToolWrapper.py
@@ -115,19 +115,14 @@ class ToolWrapper(AbstractWrapper):
-
-
-
+