Commit 9de61fa2 authored by Romain Courteaud's avatar Romain Courteaud

[erp5_web_renderjs_ui] Move include/exclude logic into form_list gadget

Next step is to activate more contextual listbox actions.

Move listbox select button to the right, to simplify cancellation.
parent 76aa608b
...@@ -7,8 +7,6 @@ ...@@ -7,8 +7,6 @@
data-i18n=Select data-i18n=Select
data-i18n=Configure data-i18n=Configure
data-i18n=Sort data-i18n=Sort
data-i18n=Include
data-i18n=Exclude
data-i18n=sample of data-i18n=sample of
data-i18n=Jump data-i18n=Jump
data-i18n=Invalid Search Criteria data-i18n=Invalid Search Criteria
......
...@@ -234,7 +234,7 @@ ...@@ -234,7 +234,7 @@
</item> </item>
<item> <item>
<key> <string>serial</string> </key> <key> <string>serial</string> </key>
<value> <string>971.30644.20500.27374</string> </value> <value> <string>971.37684.40708.23176</string> </value>
</item> </item>
<item> <item>
<key> <string>state</string> </key> <key> <string>state</string> </key>
...@@ -252,7 +252,7 @@ ...@@ -252,7 +252,7 @@
</tuple> </tuple>
<state> <state>
<tuple> <tuple>
<float>1541598213.45</float> <float>1545124793.17</float>
<string>UTC</string> <string>UTC</string>
</tuple> </tuple>
</state> </state>
......
/*jslint indent: 2, maxerr: 3, nomen: true */ /*jslint indent: 2, maxerr: 3, nomen: true */
/*global window, document, rJS, URI, RSVP, /*global window, document, rJS, URI, RSVP, console*/
SimpleQuery, ComplexQuery, Query, console, QueryFactory*/ (function (window, document, rJS, URI, RSVP, console) {
(function (window, document, rJS, URI, RSVP,
SimpleQuery, ComplexQuery, Query, console, QueryFactory) {
"use strict"; "use strict";
var variable = {}, var variable = {},
...@@ -331,6 +329,10 @@ ...@@ -331,6 +329,10 @@
.declareAcquiredMethod("renderEditorPanel", "renderEditorPanel") .declareAcquiredMethod("renderEditorPanel", "renderEditorPanel")
.declareAcquiredMethod("redirect", "redirect") .declareAcquiredMethod("redirect", "redirect")
.declareAcquiredMethod("getTranslationList", "getTranslationList") .declareAcquiredMethod("getTranslationList", "getTranslationList")
.declareAcquiredMethod("getListboxSelectActionList",
"getListboxSelectActionList")
.declareAcquiredMethod("triggerListboxSelectAction",
"triggerListboxSelectAction")
////////////////////////////////////////////// //////////////////////////////////////////////
// initialize the gadget content // initialize the gadget content
...@@ -593,7 +595,8 @@ ...@@ -593,7 +595,8 @@
current_sort, current_sort,
options, options,
url_for_option_list = [], url_for_option_list = [],
is_sortable_list = []; is_sortable_list = [],
select_list;
for (k = 0; k < column_list.length; k += 1) { for (k = 0; k < column_list.length; k += 1) {
column = column_list[k]; column = column_list[k];
...@@ -611,11 +614,26 @@ ...@@ -611,11 +614,26 @@
} }
} }
if (gadget.state.show_line_selector) {
select_list = gadget.getListboxSelectActionList()
.push(undefined, function (error) {
if (error instanceof rJS.AcquisitionError) {
// Do not break if parent gadget does not implement it
// XXX this could be a new rJS function when doing
// declareAcquiredMethod
return [];
}
throw error;
});
}
return RSVP.all([ return RSVP.all([
gadget.getUrlForList(url_for_option_list), gadget.getUrlForList(url_for_option_list),
is_sortable_list, is_sortable_list,
gadget.getTranslationList(['Jump', 'Include', 'Exclude', gadget.getTranslationList(['Jump',
'Select', 'Configure', 'Sort']) 'Select', 'Configure', 'Sort',
'Cancel']),
select_list
]); ]);
}) })
.push(function (result_list) { .push(function (result_list) {
...@@ -623,6 +641,7 @@ ...@@ -623,6 +641,7 @@
url_for_list = result_list[0], url_for_list = result_list[0],
translation_list = result_list[2], translation_list = result_list[2],
is_sortable_list = result_list[1], is_sortable_list = result_list[1],
select_option_list = result_list[3],
k, k,
url_for_index = 0, url_for_index = 0,
column, column,
...@@ -647,38 +666,30 @@ ...@@ -647,38 +666,30 @@
div_element.appendChild(h1_element); div_element.appendChild(h1_element);
if (gadget.state.show_line_selector) { if (gadget.state.show_line_selector) {
for (k = 0; k < select_option_list.length; k += 1) {
// Add include button // Add include button
// <button data-rel="hide" data-i18n="Include" name="IncludeRows" type="button" class="ui-icon-eye ui-btn-icon-left {{hide_class}}"></button> // <button data-rel="hide" data-i18n="Include" name="IncludeRows" type="button" class="ui-icon-eye ui-btn-icon-left {{hide_class}}"></button>
button_element = document.createElement('button'); button_element = document.createElement('button');
button_element.setAttribute('data-rel', 'hide'); button_element.setAttribute('data-rel', 'hide');
button_element.setAttribute('name', 'IncludeRows'); button_element.setAttribute('data-select-action', select_option_list[k].action);
button_element.setAttribute('name', 'SelectAction');
button_element.type = 'button'; button_element.type = 'button';
button_element.setAttribute('class', 'ui-icon-eye ui-btn-icon-left ' + gadget.state.hide_class); button_element.setAttribute('class', 'ui-icon-' + select_option_list[k].icon + ' ui-btn-icon-left ' + gadget.state.hide_class);
button_element.textContent = translation_list[1]; button_element.textContent = select_option_list[k].title;
div_element.appendChild(button_element); div_element.appendChild(button_element);
}
// Add exclude button // Add cancel button
// <button data-rel="hide" data-i18n="Exclude" name="ExcludeRows" type="button" class="ui-icon-low-vision ui-btn-icon-left {{hide_class}}"></button> // <button data-rel="cancel" data-i18n="Cancel" name="ExcludeRows" type="button" class="ui-icon-times ui-btn-icon-left {{hide_class}}"></button>
button_element = document.createElement('button'); button_element = document.createElement('button');
button_element.setAttribute('data-rel', 'hide'); button_element.setAttribute('data-rel', 'hide');
button_element.setAttribute('name', 'ExcludeRows'); button_element.setAttribute('name', 'CancelSelect');
button_element.type = 'button'; button_element.type = 'button';
button_element.setAttribute('class', 'ui-icon-low-vision ui-btn-icon-left ' + gadget.state.hide_class); button_element.setAttribute('class', 'ui-icon-times ui-btn-icon-left ' + gadget.state.hide_class);
button_element.textContent = translation_list[2]; button_element.textContent = translation_list[4];
div_element.appendChild(button_element); div_element.appendChild(button_element);
} else {
// Add Select button } else {
// <button {{disabled}} data-rel="hide" data-i18n="Select" name="Hide" type="button" class="ui-icon-check-square-o ui-btn-icon-left {{hide_class}}"></button>
button_element = document.createElement('button');
button_element.disabled = gadget.state.disabled;
button_element.setAttribute('data-rel', 'hide');
button_element.setAttribute('name', 'Hide');
button_element.type = 'button';
button_element.setAttribute('class', 'ui-icon-check-square-o ui-btn-icon-left ' + gadget.state.hide_class);
button_element.textContent = translation_list[3];
div_element.appendChild(button_element);
// Add Configure button // Add Configure button
// <button {{disabled}} data-rel="configure_columns" data-i18n="Configure" name="Configure" type="button" class="ui-icon-wrench ui-btn-icon-left {{configure_class}}"></button> // <button {{disabled}} data-rel="configure_columns" data-i18n="Configure" name="Configure" type="button" class="ui-icon-wrench ui-btn-icon-left {{configure_class}}"></button>
...@@ -688,7 +699,7 @@ ...@@ -688,7 +699,7 @@
button_element.setAttribute('name', 'Configure'); button_element.setAttribute('name', 'Configure');
button_element.type = 'button'; button_element.type = 'button';
button_element.setAttribute('class', 'ui-icon-wrench ui-btn-icon-left ' + gadget.state.configure_class); button_element.setAttribute('class', 'ui-icon-wrench ui-btn-icon-left ' + gadget.state.configure_class);
button_element.textContent = translation_list[4]; button_element.textContent = translation_list[2];
div_element.appendChild(button_element); div_element.appendChild(button_element);
// Add Sort button // Add Sort button
...@@ -699,7 +710,18 @@ ...@@ -699,7 +710,18 @@
button_element.setAttribute('name', 'Sort'); button_element.setAttribute('name', 'Sort');
button_element.type = 'button'; button_element.type = 'button';
button_element.setAttribute('class', 'ui-icon-sort-amount-desc ui-btn-icon-left ' + gadget.state.sort_class); button_element.setAttribute('class', 'ui-icon-sort-amount-desc ui-btn-icon-left ' + gadget.state.sort_class);
button_element.textContent = translation_list[5]; button_element.textContent = translation_list[3];
div_element.appendChild(button_element);
// Add Select button
// <button {{disabled}} data-rel="hide" data-i18n="Select" name="Hide" type="button" class="ui-icon-check-square-o ui-btn-icon-left {{hide_class}}"></button>
button_element = document.createElement('button');
button_element.disabled = gadget.state.disabled;
button_element.setAttribute('data-rel', 'hide');
button_element.setAttribute('name', 'Hide');
button_element.type = 'button';
button_element.setAttribute('class', 'ui-icon-check-square-o ui-btn-icon-left ' + gadget.state.hide_class);
button_element.textContent = translation_list[1];
div_element.appendChild(button_element); div_element.appendChild(button_element);
} }
fragment.appendChild(div_element); fragment.appendChild(div_element);
...@@ -1196,14 +1218,12 @@ ...@@ -1196,14 +1218,12 @@
sort_button = gadget.element.querySelector('button[name="Sort"]'), sort_button = gadget.element.querySelector('button[name="Sort"]'),
hide_button = gadget.element.querySelector('button[name="Hide"]'), hide_button = gadget.element.querySelector('button[name="Hide"]'),
configure_button = gadget.element.querySelector('button[name="Configure"]'), configure_button = gadget.element.querySelector('button[name="Configure"]'),
include_button = gadget.element.querySelector('button[name="IncludeRows"]'), cancel_select_button = gadget.element.querySelector('button[name="CancelSelect"]'),
exclude_button = gadget.element.querySelector('button[name="ExcludeRows"]'),
url, url,
options = {}, options = {},
all_hide_element_list, all_hide_element_list,
hide_element_list = [], checked_uid_list,
query_list = [], unchecked_uid_list,
search_query,
i; i;
if (evt.target === configure_button) { if (evt.target === configure_button) {
...@@ -1231,81 +1251,32 @@ ...@@ -1231,81 +1251,32 @@
}); });
} }
if ((evt.target === include_button) || (evt.target === exclude_button)) { if (evt.target === cancel_select_button) {
evt.preventDefault();
return gadget.changeState({
show_line_selector: false
});
}
if ((evt.target.type === 'button') &&
(evt.target.name === 'SelectAction')) {
evt.preventDefault(); evt.preventDefault();
checked_uid_list = [];
unchecked_uid_list = [];
//hide closed //hide closed
//maybe submit //maybe submit
all_hide_element_list = gadget.element.querySelectorAll(".hide_element"); all_hide_element_list = gadget.element.querySelectorAll(".hide_element");
for (i = 0; i < all_hide_element_list.length; i += 1) { for (i = 0; i < all_hide_element_list.length; i += 1) {
if (all_hide_element_list[i].checked) { if (all_hide_element_list[i].checked) {
hide_element_list.push(all_hide_element_list[i]); checked_uid_list.push(all_hide_element_list[i].getAttribute("data-uid"));
}
}
if (hide_element_list.length) {
for (i = 0; i < hide_element_list.length; i += 1) {
query_list.push(new SimpleQuery({
key: "catalog.uid",
type: "simple",
operator: (evt.target === include_button) ? "=" : "!=",
value: hide_element_list[i].getAttribute("data-uid")
}));
}
if (gadget.state.extended_search) {
search_query = QueryFactory.create(gadget.state.extended_search);
}
if (evt.target === include_button) {
// Lines must match the existing query and be one of the selected
// line. Which means that is user change the query, one of the
// selected line could disappear.
if (search_query) {
search_query = new ComplexQuery({
operator: "AND",
query_list: [
new ComplexQuery({
operator: "OR",
query_list: query_list,
type: "complex"
}),
search_query
],
type: "complex"
});
} else { } else {
search_query = new ComplexQuery({ unchecked_uid_list.push(all_hide_element_list[i].getAttribute("data-uid"));
operator: "OR",
query_list: query_list,
type: "complex"
});
} }
} else {
// Lines must match the existing query and must not be one of the
// selected line.
if (search_query) {
query_list.push(search_query);
}
search_query = new ComplexQuery({
operator: "AND",
query_list: query_list,
type: "complex"
});
}
return gadget.redirect({
command: 'store_and_change',
options: {
"extended_search": Query.objectToSearchText(search_query)
} }
}); return gadget.triggerListboxSelectAction(evt.target.getAttribute('data-select-action'), checked_uid_list, unchecked_uid_list);
} }
return gadget.changeState({
show_line_selector: false
});
}
}, false, false) }, false, false)
.declareService(function enableButton() { .declareService(function enableButton() {
...@@ -1322,5 +1293,4 @@ ...@@ -1322,5 +1293,4 @@
return; return;
}); });
}(window, document, rJS, URI, RSVP, }(window, document, rJS, URI, RSVP, console));
SimpleQuery, ComplexQuery, Query, console, QueryFactory));
...@@ -236,7 +236,7 @@ ...@@ -236,7 +236,7 @@
</item> </item>
<item> <item>
<key> <string>serial</string> </key> <key> <string>serial</string> </key>
<value> <string>971.30696.34005.13721</string> </value> <value> <string>972.27092.48077.63488</string> </value>
</item> </item>
<item> <item>
<key> <string>state</string> </key> <key> <string>state</string> </key>
...@@ -254,7 +254,7 @@ ...@@ -254,7 +254,7 @@
</tuple> </tuple>
<state> <state>
<tuple> <tuple>
<float>1541601271.99</float> <float>1545386943.7</float>
<string>UTC</string> <string>UTC</string>
</tuple> </tuple>
</state> </state>
......
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<!--
data-i18n=Include
data-i18n=Exclude
-->
<meta http-equiv="Content-type" content="text/html; charset=utf-8" /> <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width" /> <meta name="viewport" content="width=device-width" />
<title>ERP5 PT Form List</title> <title>ERP5 PT Form List</title>
...@@ -13,6 +17,7 @@ ...@@ -13,6 +17,7 @@
<!-- custom script --> <!-- custom script -->
<script src="gadget_erp5_global.js" type="text/javascript"></script> <script src="gadget_erp5_global.js" type="text/javascript"></script>
<script src="jiodev.js" type="text/javascript"></script>
<script src="gadget_erp5_pt_form_list.js" type="text/javascript"></script> <script src="gadget_erp5_pt_form_list.js" type="text/javascript"></script>
</head> </head>
......
...@@ -234,7 +234,7 @@ ...@@ -234,7 +234,7 @@
</item> </item>
<item> <item>
<key> <string>serial</string> </key> <key> <string>serial</string> </key>
<value> <string>970.13790.51027.29644</string> </value> <value> <string>972.25327.46098.50073</string> </value>
</item> </item>
<item> <item>
<key> <string>state</string> </key> <key> <string>state</string> </key>
...@@ -252,7 +252,7 @@ ...@@ -252,7 +252,7 @@
</tuple> </tuple>
<state> <state>
<tuple> <tuple>
<float>1537194684.26</float> <float>1545389700.46</float>
<string>UTC</string> <string>UTC</string>
</tuple> </tuple>
</state> </state>
......
/*global window, rJS, RSVP, calculatePageTitle */ /*global window, rJS, RSVP, calculatePageTitle, SimpleQuery, ComplexQuery,
Query, QueryFactory */
/*jslint nomen: true, indent: 2, maxerr: 3 */ /*jslint nomen: true, indent: 2, maxerr: 3 */
(function (window, rJS, RSVP, calculatePageTitle) { (function (window, rJS, RSVP, calculatePageTitle, SimpleQuery, ComplexQuery,
Query, QueryFactory) {
"use strict"; "use strict";
rJS(window) rJS(window)
...@@ -12,6 +14,7 @@ ...@@ -12,6 +14,7 @@
.declareAcquiredMethod("redirect", "redirect") .declareAcquiredMethod("redirect", "redirect")
.declareAcquiredMethod("getUrlParameter", "getUrlParameter") .declareAcquiredMethod("getUrlParameter", "getUrlParameter")
.declareAcquiredMethod("renderEditorPanel", "renderEditorPanel") .declareAcquiredMethod("renderEditorPanel", "renderEditorPanel")
.declareAcquiredMethod("getTranslationList", "getTranslationList")
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// Proxy methods to the child gadget // Proxy methods to the child gadget
...@@ -182,6 +185,98 @@ ...@@ -182,6 +185,98 @@
return gadget.redirect({command: 'store_and_change', options: options}); return gadget.redirect({command: 'store_and_change', options: options});
}); });
}, false, true); }, false, true)
}(window, rJS, RSVP, calculatePageTitle)); // Handle listbox custom button
\ No newline at end of file .allowPublicAcquisition("getListboxSelectActionList", function getListboxSelectActionList() {
return this.getTranslationList(['Include', 'Exclude'])
.push(function (result_list) {
return [{
title: result_list[0],
icon: 'eye',
action: 'include'
}, {
title: result_list[1],
icon: 'low-vision',
action: 'exclude'
}];
});
})
.allowPublicAcquisition("triggerListboxSelectAction", function triggerListboxSelectAction(argument_list) {
var action = argument_list[0],
uid_list = argument_list[1],
gadget = this,
i,
search_query,
query_list = [];
if ((action === 'include') || (action === 'exclude')) {
if (uid_list.length === 0) {
// XXX Queries do not correctly handle empty uid list
return gadget.redirect({
command: 'reload'
});
}
for (i = 0; i < uid_list.length; i += 1) {
query_list.push(new SimpleQuery({
key: "catalog.uid",
type: "simple",
operator: (action === 'include') ? "=" : "!=",
value: uid_list[i]
}));
}
if (gadget.state.extended_search) {
search_query = QueryFactory.create(gadget.state.extended_search);
}
if (action === 'include') {
// Lines must match the existing query and be one of the selected
// line. Which means that is user change the query, one of the
// selected line could disappear.
if (search_query) {
search_query = new ComplexQuery({
operator: "AND",
query_list: [
new ComplexQuery({
operator: "OR",
query_list: query_list,
type: "complex"
}),
search_query
],
type: "complex"
});
} else {
search_query = new ComplexQuery({
operator: "OR",
query_list: query_list,
type: "complex"
});
}
} else {
// Lines must match the existing query and must not be one of the
// selected line.
if (search_query) {
query_list.push(search_query);
}
search_query = new ComplexQuery({
operator: "AND",
query_list: query_list,
type: "complex"
});
}
return gadget.redirect({
command: 'store_and_change',
options: {
"extended_search": Query.objectToSearchText(search_query)
}
});
}
throw new Error('Unsupported triggerListboxSelectAction action: ' + action);
});
}(window, rJS, RSVP, calculatePageTitle, SimpleQuery, ComplexQuery, Query,
QueryFactory));
\ No newline at end of file
...@@ -230,7 +230,7 @@ ...@@ -230,7 +230,7 @@
</item> </item>
<item> <item>
<key> <string>serial</string> </key> <key> <string>serial</string> </key>
<value> <string>971.20638.49773.13568</string> </value> <value> <string>972.27121.4308.45824</string> </value>
</item> </item>
<item> <item>
<key> <string>state</string> </key> <key> <string>state</string> </key>
...@@ -248,7 +248,7 @@ ...@@ -248,7 +248,7 @@
</tuple> </tuple>
<state> <state>
<tuple> <tuple>
<float>1540997693.31</float> <float>1545232470.9</float>
<string>UTC</string> <string>UTC</string>
</tuple> </tuple>
</state> </state>
......
...@@ -1428,22 +1428,22 @@ ...@@ -1428,22 +1428,22 @@
</tr> </tr>
<tr> <tr>
<td>waitForElementPresent</td> <td>waitForElementPresent</td>
<td>//div[contains(@data-gadget-url, 'gadget_erp5_field_listbox.html')]//button[@name='IncludeRows']</td> <td>//div[contains(@data-gadget-url, 'gadget_erp5_field_listbox.html')]//button[@name='SelectAction' and @data-select-action='include']</td>
<td></td> <td></td>
</tr> </tr>
<tr> <tr>
<td>assertElementPresent</td> <td>assertElementPresent</td>
<td>//div[contains(@data-gadget-url, 'gadget_erp5_field_listbox.html')]//button[@name='IncludeRows']</td> <td>//div[contains(@data-gadget-url, 'gadget_erp5_field_listbox.html')]//button[@name='SelectAction' and @data-select-action='include']</td>
<td></td> <td></td>
</tr> </tr>
<tr> <tr>
<td>click</td> <td>click</td>
<td>//div[contains(@data-gadget-url, 'gadget_erp5_field_listbox.html')]//button[@name='IncludeRows']</td> <td>//div[contains(@data-gadget-url, 'gadget_erp5_field_listbox.html')]//button[@name='SelectAction' and @data-select-action='include']</td>
<td></td> <td></td>
</tr> </tr>
<tr> <tr>
<td>waitForElementNotPresent</td> <td>waitForElementNotPresent</td>
<td>//div[contains(@data-gadget-url, 'gadget_erp5_field_listbox.html')]//button[@name='IncludeRows']</td> <td>//div[contains(@data-gadget-url, 'gadget_erp5_field_listbox.html')]//button[@name='SelectAction' and @data-select-action='include']</td>
<td></td> <td></td>
</tr> </tr>
<tr> <tr>
...@@ -1457,22 +1457,22 @@ ...@@ -1457,22 +1457,22 @@
</tr> </tr>
<tr> <tr>
<td>waitForElementPresent</td> <td>waitForElementPresent</td>
<td>//div[contains(@data-gadget-url, 'gadget_erp5_field_listbox.html')]//button[@name='ExcludeRows']</td> <td>//div[contains(@data-gadget-url, 'gadget_erp5_field_listbox.html')]//button[@name='SelectAction' and @data-select-action='exclude']</td>
<td></td> <td></td>
</tr> </tr>
<tr> <tr>
<td>assertElementPresent</td> <td>assertElementPresent</td>
<td>//div[contains(@data-gadget-url, 'gadget_erp5_field_listbox.html')]//button[@name='ExcludeRows']</td> <td>//div[contains(@data-gadget-url, 'gadget_erp5_field_listbox.html')]//button[@name='SelectAction' and @data-select-action='exclude']</td>
<td></td> <td></td>
</tr> </tr>
<tr> <tr>
<td>click</td> <td>click</td>
<td>//div[contains(@data-gadget-url, 'gadget_erp5_field_listbox.html')]//button[@name='ExcludeRows']</td> <td>//div[contains(@data-gadget-url, 'gadget_erp5_field_listbox.html')]//button[@name='SelectAction' and @data-select-action='exclude']</td>
<td></td> <td></td>
</tr> </tr>
<tr> <tr>
<td>waitForElementNotPresent</td> <td>waitForElementNotPresent</td>
<td>//div[contains(@data-gadget-url, 'gadget_erp5_field_listbox.html')]//button[@name='ExcludeRows']</td> <td>//div[contains(@data-gadget-url, 'gadget_erp5_field_listbox.html')]//button[@name='SelectAction' and @data-select-action='exclude']</td>
<td></td> <td></td>
</tr> </tr>
<tr> <tr>
...@@ -1486,12 +1486,12 @@ ...@@ -1486,12 +1486,12 @@
</tr> </tr>
<tr> <tr>
<td>assertElementNotPresent</td> <td>assertElementNotPresent</td>
<td>//div[contains(@data-gadget-url, 'gadget_erp5_field_listbox.html')]//button[@name='IncludeRows']</td> <td>//div[contains(@data-gadget-url, 'gadget_erp5_field_listbox.html')]//button[@name='SelectAction' and @data-select-action='include']</td>
<td></td> <td></td>
</tr> </tr>
<tr> <tr>
<td>assertElementNotPresent</td> <td>assertElementNotPresent</td>
<td>//div[contains(@data-gadget-url, 'gadget_erp5_field_listbox.html')]//button[@name='ExcludeRows']</td> <td>//div[contains(@data-gadget-url, 'gadget_erp5_field_listbox.html')]//button[@name='SelectAction' and @data-select-action='exclude']</td>
<td></td> <td></td>
</tr> </tr>
<tr> <tr>
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment