Commit 2dc9f19a authored by Jérome Perrin's avatar Jérome Perrin

Update officejs support request app for strict CSP

This is a first step to stop using "unsafe" web sections.

This updates support request app to not require `script-src: unsafe-eval` and `style-src: unsafe-inline` in the CSP.
Dropping `script-src: unsafe-eval` is made possible by using domsugar instead of handlebars for dynamic content. Dropping `style-src: unsafe-inline` by using CSS files instead of inline `style` attributes in the DOM. One minor regression is that the tooltips from the graph on the front page gadget will cause warning because of `unsafe-inline` and not render the series color.

This application was also modernized a bit, it now uses the HTML viewer gadget to display post contents and supports translation.

See merge request nexedi/erp5!1821
parents 8e3d2599 21828d69
......@@ -1405,6 +1405,12 @@ msgstr "Attacher un document"
msgid "Attachment"
msgstr "Pièce jointe"
msgid "Attachment:"
msgstr "Pièce jointe:"
msgid "Attachment: "
msgstr "Pièce jointe: "
msgid "Attachments"
msgstr "Pièces jointes"
......@@ -1897,6 +1903,9 @@ msgstr "Le business template a été mis à jour avec succès"
msgid "Buyer"
msgstr "Acheteur"
msgid "By"
msgstr "Par"
msgid "CAD Library"
msgstr "Nom du fichier Lectra"
......@@ -2722,6 +2731,9 @@ msgstr "Commentaires entrés par un utilisateur. Les commentaires ne seront visi
msgid "Comments which can be read by internal users but which, unlike description, are not printed out on official documents."
msgstr "Commentaires accessibles aux utilisateurs internes de l'application qui, contrairement à la description, ne sont pas repris ou imprimés sur les documents officiels."
msgid "Comments:"
msgstr "Commentaires:"
msgid "Compare"
msgstr "Comparer"
......@@ -6586,6 +6598,9 @@ msgstr "Dernier"
msgid "Last In, First Out"
msgstr "Dernier entré, premier sorti"
msgid "Last Month Activity"
msgstr "Activité du mois"
msgid "Last Name"
msgstr "Nom"
......@@ -10309,6 +10324,9 @@ msgstr "Hiérarchie d'expression de besoins"
msgid "Reset"
msgstr "Réinitialiser"
msgid "Reset Filter"
msgstr "Réinitialiser le filtre"
msgid "Reset List Setting"
msgstr "Réinitialiser la configuration de la liste"
......@@ -11740,6 +11758,9 @@ msgstr "Soumettre au manager (action)"
msgid "Submit Message"
msgstr "Soumettre le message"
msgid "Submit New Support Request"
msgstr "Soumettre une nouvelle demande d'assistance"
msgid "Submit Project"
msgstr "Soumettre le projet"
......@@ -11905,6 +11926,9 @@ msgstr "Rapport détaillé"
msgid "Support Request Module"
msgstr "Module des demandes d'assistance"
msgid "Support Request Pipe"
msgstr "Demandes d'assistance en cours"
msgid "Support Request Status"
msgstr "État"
......@@ -11917,6 +11941,9 @@ msgstr "Reference du type de la demande d'assistance"
msgid "Support Request Types"
msgstr "Types des demandes d'assistance"
msgid "Support Request WorkLists"
msgstr "Listes de travail des demandes d'assistance"
msgid "Support Requests"
msgstr "Demandes d'assistance"
......
html {
height: 300px;
}
body {
height: 100%;
margin: 0;
}
.graph-content {
height: 95%;
width: 95%;
}
\ No newline at end of file
......@@ -2,7 +2,7 @@
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Web Manifest" module="erp5.portal_type"/>
<global name="Web Style" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
......@@ -23,7 +23,6 @@
<key> <string>_Add_portal_content_Permission</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Manager</string>
</tuple>
......@@ -42,8 +41,6 @@
<key> <string>_Modify_portal_content_Permission</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Manager</string>
</tuple>
</value>
......@@ -61,14 +58,6 @@
</tuple>
</value>
</item>
<item>
<key> <string>categories</string> </key>
<value>
<tuple>
<string>contributor/person_module/1</string>
</tuple>
</value>
</item>
<item>
<key> <string>content_md5</string> </key>
<value>
......@@ -77,11 +66,13 @@
</item>
<item>
<key> <string>content_type</string> </key>
<value> <string>text/plain</string> </value>
<value>
<none/>
</value>
</item>
<item>
<key> <string>default_reference</string> </key>
<value> <string>gadget_field_graph_echarts.appcache</string> </value>
<value> <string>gadget_field_graph_echarts.css</string> </value>
</item>
<item>
<key> <string>description</string> </key>
......@@ -91,44 +82,21 @@
</item>
<item>
<key> <string>id</string> </key>
<value> <string>gadget_field_graph_echarts_appcache</string> </value>
<value> <string>gadget_field_graph_echarts_css</string> </value>
</item>
<item>
<key> <string>language</string> </key>
<value> <string>en</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Web Manifest</string> </value>
</item>
<item>
<key> <string>short_title</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>text_content</string> </key>
<value> <string>CACHE MANIFEST\n
# v1.1.1\n
CACHE:\n
gadget_field_graph_echarts.html/echarts-all.js\n
gadget_field_graph_echarts.html/gadget_global.js\n
gadget_field_graph_echarts.html/renderjs.js\n
gadget_field_graph_echarts.html/rsvp.js\n
gadget_field_graph_echarts.html/unsafe/gadget_field_graph_echarts.js\n
NETWORK:\n
*</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Officejs ECharts Cache</string> </value>
</item>
<item>
<key> <string>url_string</string> </key>
<value>
<none/>
</value>
<value> <string>Field Graph With Echarts CSS</string> </value>
</item>
<item>
<key> <string>version</string> </key>
......@@ -190,11 +158,11 @@ NETWORK:\n
<dictionary>
<item>
<key> <string>action</string> </key>
<value> <string>publish_alive</string> </value>
<value> <string>publish</string> </value>
</item>
<item>
<key> <string>actor</string> </key>
<value> <string>zope</string> </value>
<value> <string>ERP5TypeTestCase</string> </value>
</item>
<item>
<key> <string>comment</string> </key>
......@@ -218,7 +186,7 @@ NETWORK:\n
</tuple>
<state>
<tuple>
<float>1506525352.34</float>
<float>1694150959.89</float>
<string>UTC</string>
</tuple>
</state>
......@@ -227,7 +195,7 @@ NETWORK:\n
</item>
<item>
<key> <string>validation_state</string> </key>
<value> <string>published_alive</string> </value>
<value> <string>published</string> </value>
</item>
</dictionary>
</list>
......@@ -253,7 +221,7 @@ NETWORK:\n
</item>
<item>
<key> <string>actor</string> </key>
<value> <string>zope</string> </value>
<value> <string>ERP5TypeTestCase</string> </value>
</item>
<item>
<key> <string>comment</string> </key>
......@@ -267,7 +235,7 @@ NETWORK:\n
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>962.25486.15124.29883</string> </value>
<value> <string>1010.64822.48938.57036</string> </value>
</item>
<item>
<key> <string>state</string> </key>
......@@ -287,8 +255,8 @@ NETWORK:\n
</tuple>
<state>
<tuple>
<float>1538646068.83</float>
<string>GMT+9</string>
<float>1694152130.18</float>
<string>UTC</string>
</tuple>
</state>
</object>
......@@ -314,11 +282,13 @@ NETWORK:\n
<dictionary>
<item>
<key> <string>action</string> </key>
<value> <string>detect_converted_file</string> </value>
<value>
<none/>
</value>
</item>
<item>
<key> <string>actor</string> </key>
<value> <string>zope</string> </value>
<value> <string>ERP5TypeTestCase</string> </value>
</item>
<item>
<key> <string>comment</string> </key>
......@@ -330,7 +300,7 @@ NETWORK:\n
</item>
<item>
<key> <string>external_processing_state</string> </key>
<value> <string>converted</string> </value>
<value> <string>empty</string> </value>
</item>
<item>
<key> <string>serial</string> </key>
......@@ -350,7 +320,7 @@ NETWORK:\n
</tuple>
<state>
<tuple>
<float>1506524633.84</float>
<float>1694150865.83</float>
<string>UTC</string>
</tuple>
</state>
......
<!DOCTYPE html>
<html manifest="gadget_field_graph_echarts.appcache" style="height: 300px">
<html>
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width" />
......@@ -9,19 +9,20 @@
<link rel="http://www.renderjs.org/rel/interface" href="gadget_field_graph_interface.html">
<!-- renderjs -->
<script src="rsvp.js" type="text/javascript"></script>
<script src="renderjs.js" type="text/javascript"></script>
<script src="rsvp.js"></script>
<script src="renderjs.js"></script>
<!-- libraries needed for graphs -->
<script src="echarts-all.js" type="text/javascript"></script>
<script src="echarts-all.js"></script>
<!-- custom script -->
<script src="gadget_global.js" type="text/javascript"></script>
<script src="unsafe/gadget_field_graph_echarts.js" type="text/javascript"></script>
<script src="gadget_global.js"></script>
<script src="gadget_field_graph_echarts.js"></script>
<link rel="stylesheet" type="text/css" href="gadget_field_graph_echarts.css">
</head>
<body style="height: 100%; margin: 0">
<div class="graph-content" style="height: 95%; width: 95%" disabled>
<body>
<div class="graph-content" disabled>
</div>
</body>
</html>
\ No newline at end of file
......@@ -64,9 +64,7 @@
<item>
<key> <string>categories</string> </key>
<value>
<tuple>
<string>classification/collaborative/team</string>
</tuple>
<tuple/>
</value>
</item>
<item>
......@@ -268,8 +266,8 @@
</tuple>
<state>
<tuple>
<float>1538645729.6</float>
<string>GMT+9</string>
<float>1694152073.26</float>
<string>UTC</string>
</tuple>
</state>
</object>
......
......@@ -64,9 +64,7 @@
<item>
<key> <string>categories</string> </key>
<value>
<tuple>
<string>contributor/person_module/1</string>
</tuple>
<tuple/>
</value>
</item>
<item>
......
......@@ -66,6 +66,9 @@
for (j = 0; j < sp_list.length; j += 1) {
sp_select.options[j] = new Option(sp_list[j][0], sp_list[j][1]);
}
if (sp_select.options.length === 2) {
sp_select.selectedIndex = 1;
}
});
})
.onEvent('change', function (evt) {
......
......@@ -264,7 +264,7 @@
</tuple>
<state>
<tuple>
<float>1607511772.31</float>
<float>1607511772.32</float>
<string>GMT+1</string>
</tuple>
</state>
......
......@@ -26,11 +26,11 @@
line-height: 1.5;
}
#wrap1 iframe {
height: 100%;
#wrap1, #wrap2 {
height: 300px;
}
#wrap2 iframe {
#wrap1 iframe, #wrap2 iframe {
height: 100%;
}
......@@ -40,6 +40,12 @@
width: 120px;
}
.gadget-content .ui-field-contain > .left,
.gadget-content .ui-field-contain > .right {
height: 50%;
width: 100%;
}
.gadget-content .ui-field-contain .bottom .first-line-buttons {
-webkit-appearance: none;
margin-top: 0;
......
......@@ -268,7 +268,7 @@
</tuple>
<state>
<tuple>
<float>1505897718.64</float>
<float>1694151687.28</float>
<string>UTC</string>
</tuple>
</state>
......
<!DOCTYPE html>
<html>
<!--
data-i18n=Homepage
data-i18n="Count"
data-i18n="Customer Support Dashboard"
data-i18n="Days"
data-i18n="Homepage"
data-i18n="Last Month Activity"
data-i18n="Support Request Pipe"
-->
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
......@@ -26,14 +31,14 @@
</div>
<div class="ui-field-contain">
<div class="left" style="height:50%; width:100%">
<div class="left">
<div class="ui-icon-spinner ui-btn-icon-notext first-loader graph-spinner"></div>
<div id="wrap1" style="height:300px;"></div>
<div id="wrap1"></div>
</div>
<div class="right" style="height:50%; width:100%">
<div class="right">
<div class="ui-icon-spinner ui-btn-icon-notext first-loader graph-spinner"></div>
<div id="wrap2" style="height:300px;"></div>
<div id="wrap2"></div>
</div>
</div>
......@@ -42,7 +47,7 @@
</div>
<div data-gadget-url="gadget_erp5_page_form.html" data-gadget-scope="last"></div>
<h1 data-i18n="[value]Support Request WorkLists" class="ui-title ui-override-theme worklist-title">Support Request WorkLists</h1>
<h1 data-i18n="Support Request WorkLists" class="ui-title ui-override-theme worklist-title">Support Request WorkLists</h1>
<div data-gadget-url="gadget_supportrequest_page_worklist.html" data-gadget-scope="worklist"></div>
</body>
</html>
......@@ -260,7 +260,7 @@
</tuple>
<state>
<tuple>
<float>1613489292.9</float>
<float>1694151614.07</float>
<string>UTC</string>
</tuple>
</state>
......
/*global document, window, Option, rJS, RSVP, promiseEventListener */
/*global document, window, Option, rJS, RSVP */
/*jslint nomen: true, indent: 2, maxerr: 3 */
(function (window, rJS, RSVP, promiseEventListener, loopEventListener) {
(function (window, rJS, RSVP) {
"use strict";
function getActionListByName(action_object, name) {
......@@ -47,7 +47,8 @@
/////////////////////////////////////////////////////////////////
.declareAcquiredMethod("jio_getAttachment", "jio_getAttachment")
.declareAcquiredMethod("getUrlParameter", "getUrlParameter")
.declareAcquiredMethod("translateHtml", "translateHtml")
.declareAcquiredMethod("translate", "translate")
.declareAcquiredMethod("getTranslationList", "getTranslationList")
.declareAcquiredMethod("redirect", "redirect")
.declareAcquiredMethod("updateHeader", "updateHeader")
.declareAcquiredMethod("updateConfiguration", "updateConfiguration")
......@@ -89,7 +90,8 @@
search_criteria = '( translated_simulation_state_title: "' + seriesName + '" AND delivery.start_date: >= ' + begin_date.toISOString().slice(0, 10) + ' AND delivery.start_date: < ' + end_date.toISOString().slice(0, 10) + ' )';
} else {
// Situation 2: Search Support Request by state with limit of 30 days.
search_criteria = '( translated_simulation_state_title: "' + name + '" AND delivery.start_date: >= ' + days_30.toISOString().slice(0, 10) + ' )';
end_date = days_30;
search_criteria = '( translated_simulation_state_title: "' + name + '" AND delivery.start_date: >= ' + end_date.toISOString().slice(0, 10) + ' )';
}
return search_criteria;
})
......@@ -133,8 +135,21 @@
});
})
.push(function () {
return gadget.translate('Customer Support Dashboard')
.push(function (translated_text) {
return gadget.updateHeader({
page_title: 'Customer Support Dashboard'
page_title: translated_text
});
});
}).push(function () {
return gadget.getTranslationList([
"Submit New Support Request",
"Reset Filter",
"Support Request WorkLists"
]).push(function (translation_list) {
gadget.element.querySelector('[data-i18n="[value]Submit New Support Request"]').value = translation_list[0];
gadget.element.querySelector('[data-i18n="[value]Reset Filter"]').value = translation_list[1];
gadget.element.querySelector('[data-i18n="Support Request WorkLists"]').innerText = translation_list[2];
});
});
})
......@@ -163,7 +178,10 @@
sandbox: "iframe",
element: gadget.property_dict.element.querySelector("#wrap2")
}
)
),
gadget.translate("Support Request Pipe"),
gadget.translate("Days"),
gadget.translate("Count")
]);
})
.push(function (result) {
......@@ -180,10 +198,12 @@
var sp_data = result_list[0],
graph_gadget_1 = result_list[1],
graph_gadget_2 = result_list[2],
translated_message_support_request_pipe = result_list[3],
translated_message_days = result_list[4],
translated_message_count = result_list[5],
count_by_state_and_date_range = sp_data.count_by_state_and_date_range;
return RSVP.all([
// render first graph
graph_gadget_1.render({
return RSVP.all([graph_gadget_1.render(
{
value: {
data: [
{
......@@ -216,16 +236,24 @@
],
layout: {
axis_dict : {
'0': {"title": "Days"},
'1': {"title": "Number", "value_type": "number"}
'0': {"title": translated_message_days},
'1': {"title": translated_message_count, "value_type": "number"}
},
title: "Support Request Pipe"
title: translated_message_support_request_pipe
}
}
}),
// render second graph
graph_gadget_2.render({
}
),
sp_data,
graph_gadget_2,
gadget.translate("Last Month Activity")
]);
})
.push(function (result_list) {
var sp_data = result_list[1],
graph_gadget = result_list[2],
translated_message_last_month_activity = result_list[3];
return graph_gadget.render({
value:
{
data: [
......@@ -254,12 +282,10 @@
0: {"title": "date"},
1: {"title": "value", "value_type": "number"}
},
title: "Last Month Activity"
title: translated_message_last_month_activity
}
}
})
]);
});
});
})
.declareService(function () {
......@@ -278,7 +304,7 @@
return new RSVP.Queue()
.push(function () {
var restore_filter_input = gadget.element.querySelectorAll("input")[1],
one = loopEventListener(restore_filter_input, "click", false, function () {
one = rJS.loopEventListener(restore_filter_input, "click", false, function () {
restore_filter_input.disabled = true;
restore_filter_input.classList.add("ui-disabled");
return gadget.redirect({
......@@ -311,8 +337,7 @@
view_list = erp5_document._links.view || [];
gadget.property_dict.option_dict = {
// graph_gadget: Keep ending slash to be consistent with the automatically set "base" tag
graph_gadget: "unsafe/gadget_field_graph_echarts.html/",
graph_gadget: "gadget_field_graph_echarts.html",
listbox_gadget: getActionListByName(view_list, "view_last_support_request")[0].href,
listbox_jio_key: "support_request_module",
field_listbox_begin_from: field_listbox_begin_from
......@@ -344,4 +369,4 @@
});
});
}(window, rJS, RSVP, promiseEventListener, rJS.loopEventListener));
}(window, rJS, RSVP));
......@@ -248,7 +248,7 @@
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>989.27549.43950.31078</string> </value>
<value> <string>1010.61995.46538.53572</string> </value>
</item>
<item>
<key> <string>state</string> </key>
......@@ -268,7 +268,7 @@
</tuple>
<state>
<tuple>
<float>1613149261.83</float>
<float>1694082778.57</float>
<string>UTC</string>
</tuple>
</state>
......
<!DOCTYPE html>
<html>
<!--
data-i18n=Worklist
data-i18n=All work caught up!
-->
<head>
<!--
data-i18n="All work caught up!"
-->
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width" />
<title>ERP5 Page Worklist</title>
<!-- renderjs -->
<script src="rsvp.js" type="text/javascript"></script>
<script src="renderjs.js" type="text/javascript"></script>
<script src="handlebars.js" type="text/javascript"></script>
<script src="jiodev.js" type="text/javascript"></script>
<script src="rsvp.js"></script>
<script src="renderjs.js"></script>
<script src="jiodev.js"></script>
<!-- custom script -->
<script src="gadget_global.js" type="text/javascript"></script>
<script src="gadget_supportrequest_page_worklist.js" type="text/javascript"></script>
<!-- XXX must set theme here! -->
<script id="table-template" type="text/x-handlebars-template">
{{#if document_list }}
<ul data-role="listview" data-theme="c" class="document-listview ui-listview-inset ui-corner-all">
{{#each document_list}}
<li class="ui-li-has-count" data-icon="false" ><a class="ui-body-inherit" href="{{link}}">{{title}} <span class="ui-li-count">{{count}}</span></a></li>
{{/each}}
</ul>
{{else}}
<p>All work caught up!</p>
{{/if}}
</script>
<script src="domsugar.js"></script>
<script src="gadget_global.js"></script>
<script src="gadget_supportrequest_page_worklist.js"></script>
</head>
<body>
<section class="document_list"></section>
......
......@@ -260,7 +260,7 @@
</tuple>
<state>
<tuple>
<float>1506514266.01</float>
<float>1694060576.91</float>
<string>UTC</string>
</tuple>
</state>
......
/*global window, rJS, RSVP, Handlebars, Query */
/*global window, rJS, RSVP, Query, domsugar */
/*jslint nomen: true, indent: 2, maxerr: 3 */
(function (window, rJS, RSVP, Handlebars, Query) {
(function (window, rJS, RSVP, Query, domsugar) {
"use strict";
/////////////////////////////////////////////////////////////////
// Handlebars
/////////////////////////////////////////////////////////////////
// Precompile the templates while loading the first gadget instance
var gadget_klass = rJS(window),
source = gadget_klass.__template_element
.getElementById("table-template")
.innerHTML,
table_template = Handlebars.compile(source);
gadget_klass
rJS(window)
/////////////////////////////////////////////////////////////////
// Acquired methods
/////////////////////////////////////////////////////////////////
.declareAcquiredMethod("jio_getAttachment", "jio_getAttachment")
.declareAcquiredMethod("translateHtml", "translateHtml")
.declareAcquiredMethod("translate", "translate")
.declareAcquiredMethod("updateHeader", "updateHeader")
.declareAcquiredMethod("getSetting", "getSetting")
.declareAcquiredMethod("getUrlFor", "getUrlFor")
......@@ -70,17 +60,54 @@
return RSVP.all(promise_list);
})
.push(function (result_list) {
var line_list = [], i;
for (i = 0; i < result_list.length; i += 1) {
line_list.push({
link: result_list[i][0],
title: result_list[i][1],
count: result_list[i][2]
});
if (result_list.length) {
return domsugar(
"ul",
{
"data-role": "listview",
"data-theme": "c",
"class": "document-listview ui-listview-inset ui-corner-all"
},
result_list.map(function (r) {
var link = r[0], title = r[1], count = r[2];
return domsugar(
"li",
{
"class": "ui-li-has-count",
"data-icon": "false"
},
[
domsugar(
"a",
{
"class": "ui-body-inherit",
"href": link
},
[
title,
" ",
domsugar(
"span",
{ "class": "ui-li-count" },
[count.toString()]
)
]
)
]
);
})
);
}
gadget.element.querySelector('.document_list').innerHTML = table_template({
document_list: line_list
return gadget.translate("All work caught up!")
.push(function (messageNoWorklist) {
return domsugar("p", [messageNoWorklist]);
});
})
.push(function (dom_list) {
domsugar(
gadget.element.querySelector('.document_list'),
[dom_list]
);
});
});
}(window, rJS, RSVP, Handlebars, Query));
\ No newline at end of file
}(window, rJS, RSVP, Query, domsugar));
\ No newline at end of file
......@@ -260,7 +260,7 @@
</tuple>
<state>
<tuple>
<float>1506505200.17</float>
<float>1694062935.94</float>
<string>UTC</string>
</tuple>
</state>
......
......@@ -3,75 +3,54 @@
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width" />
<title>ERP5 Panel</title>
<title>Support Requests Panel</title>
<link rel="http://www.renderjs.org/rel/interface" href="interface_panel.html">
<!--
data-i18n="Home"
data-i18n="Support Requests"
data-i18n="Preferences"
data-i18n="Logout"
data-i18n="Views"
data-i18n="Decisions"
data-i18n="Actions"
data-i18n="Jumps"
-->
<!-- renderjs -->
<script src="rsvp.js" type="text/javascript"></script>
<script src="renderjs.js" type="text/javascript"></script>
<script src="handlebars.js" type="text/javascript"></script>
<script src="gadget_global.js" type="text/javascript"></script>
<script src="rsvp.js"></script>
<script src="renderjs.js"></script>
<script src="domsugar.js"></script>
<script src="jiodev.js"></script>
<script src="gadget_global.js"></script>
<script src="gadget_erp5_global.js"></script>
<script id="panel-template-header" type="text/x-handlebars-template">
<div data-role="header" class="ui-bar-inherit">
<div class="ui-controlgroup ui-controlgroup-horizontal ui-btn-left">
<!-- custom script -->
<script src="gadget_supportrequest_panel.js"></script>
</head>
<body>
<div>
<div data-role="header">
<div class="ui-btn-left">
<div class="ui-controlgroup-controls">
<button data-i18n="Close" class="ui-btn ui-btn-icon-notext ui-icon-delete">Close</button>
<button data-i18n="Close" class="ui-btn-icon-notext ui-icon-delete">Close</button>
</div>
</div>
<div class="panel_img">
<img class="ui-title" alt="ERP5" src="gadget_erp5_panel.png"/>
<img alt="ERP5" src="gadget_erp5_panel.png"/>
</div>
</div>
</script>
<div>
<script id="panel-template-body" type="text/x-handlebars-template">
<div class="ui-content">
<form class="dialog_form">
<button type="submit" class="ui-btn ui-btn-b ui-btn-inline
ui-icon-action ui-btn-icon-right ui-screen-hidden">Submit</button>
<form>
<button type="submit" class="ui-icon-action ui-btn-icon-right ui-screen-hidden">Submit</button>
<div data-gadget-url="gadget_erp5_searchfield.html"
data-gadget-scope="erp5_searchfield"
data-gadget-sandbox="public"></div>
</form>
<ul data-role="listview" class="ui-listview" data-enhanced="true">
<li class="ui-first-child"><a href="#" class="ui-btn ui-btn-icon-left ui-icon-home" data-i18n="Home">Home</a></li>
<li><a href="{{supportrequest_href}}" class="ui-btn ui-btn-icon-left ui-icon-life-ring" data-i18n="Support Requests">Support Requests</a></li>
<li><a href="{{preference_href}}" class="ui-btn ui-btn-icon-left ui-icon-sliders" data-i18n="Preferences">Preferences</a></li>
<li class="ui-last-child"><a href="{{logout_href}}" class="ui-btn ui-btn-icon-left ui-icon-power-off" data-i18n="Logout" accesskey="o">Logout</a></li>
</ul>
<ul></ul>
<dl></dl>
</div>
</script>
<script id="panel-template-body-desktop" type="text/x-handlebars-template">
<dt class="ui-content-title ui-body-c ui-btn ui-btn-icon-left ui-icon-eye" data-i18n="Views">Views</dt>
{{#each view_list}}
<dd data-role="listview" data-theme="c" data-inset="true" class="document-listview">
<a data-i18n="{{title}}" class="ui-body-inherit" href="{{href}}">{{title}}</a>
</dd>
{{/each}}
<dt class="ui-content-title ui-body-c ui-btn ui-btn-icon-left ui-icon-cogs" data-i18n="Decisions">Decisions</dt>
{{#each workflow_list}}
<dd data-role="listview" data-theme="c" data-inset="true" class="document-listview">
<a data-i18n="{{title}}" class="ui-body-inherit" href="{{href}}">{{title}}</a>
</dd>
{{/each}}
<dt class="ui-content-title ui-body-c ui-btn ui-btn-icon-left ui-icon-plane" data-i18n="Jumps">Jumps</dt>
{{#each jump_list}}
<dd data-role="listview" data-theme="c" data-inset="true" class="document-listview">
<a data-i18n="{{title}}" class="ui-body-inherit" href="{{href}}">{{title}}</a>
</dd>
{{/each}}
</script>
<!-- custom script -->
<script src="gadget_supportrequest_panel.js" type="text/javascript"></script>
</head>
<body>
<div class="jqm-navmenu-panel"></div>
</div>
</body>
</html>
\ No newline at end of file
......@@ -260,7 +260,7 @@
</tuple>
<state>
<tuple>
<float>1625463570.56</float>
<float>1694148611.08</float>
<string>UTC</string>
</tuple>
</state>
......
/*jslint nomen: true, indent: 2, maxerr: 3 */
/*global window, document, rJS, Handlebars, RSVP, Node */
(function (window, document, rJS, Handlebars, RSVP, Node, loopEventListener) {
/*jslint nomen: true, indent: 2, maxerr: 3, unparam: true */
/*global window, document, rJS, RSVP, Node, asBoolean , ensureArray,
mergeGlobalActionWithRawActionList, domsugar*/
(function (window, document, rJS, RSVP, Node, asBoolean, ensureArray,
mergeGlobalActionWithRawActionList, domsugar) {
"use strict";
/////////////////////////////////////////////////////////////////
// temlates
/////////////////////////////////////////////////////////////////
// Precompile templates while loading the first gadget instance
var gadget_klass = rJS(window),
template_element = gadget_klass.__template_element,
panel_template_header = Handlebars.compile(template_element
.getElementById("panel-template-header")
.innerHTML),
panel_template_body = Handlebars.compile(template_element
.getElementById("panel-template-body")
.innerHTML),
panel_template_body_desktop = Handlebars.compile(template_element
.getElementById("panel-template-body-desktop")
.innerHTML);
/**
* This gadget is same as web_page_module/rjs_gadget_erp5_panel_js with the following differences:
* - only Home, Support Requests, Preferences and Logout actions ( no "Modules", "Worklists", "History", ... )
* - no [ ] editable checkbox
* - object actions does not show "Actions" and "Workflows" is named "Decision"
* - search only search support requests
*/
function appendDt(fragment, dt_title, dt_icon,
action_list, href_list, index) {
// <dt class="ui-btn-icon-left ui-icon-eye">Views</dt>
// {{#each view_list}}
// <dd class="document-listview">
// <a class="{{class_name}}" href="{{href}}">{{title}}</a>
// </dd>
// {{/each}}
//////////////////////
var element_list = [
domsugar('dt', {
text: dt_title,
'class': 'ui-btn-icon-left ui-icon-' + dt_icon
})
],
i;
for (i = 0; i < action_list.length; i += 1) {
element_list.push(domsugar('dd', {'class': 'document-listview'}, [
domsugar('a', {
href: href_list[index + i],
text: action_list[i].title,
'class': action_list[i].class_name || null
})
]));
}
fragment.appendChild(domsugar(null, element_list));
}
gadget_klass
rJS(window)
.setState({
visible: false,
desktop: false
visible: false
})
//////////////////////////////////////////////
// acquired method
//////////////////////////////////////////////
.declareAcquiredMethod("jio_getAttachment", "jio_getAttachment")
.declareAcquiredMethod("getUrlFor", "getUrlFor")
.declareAcquiredMethod("translateHtml", "translateHtml")
.declareAcquiredMethod("getSetting", "getSetting")
.declareAcquiredMethod("getUrlForList", "getUrlForList")
.declareAcquiredMethod("getTranslationList", "getTranslationList")
.declareAcquiredMethod("getTranslationDict", "getTranslationDict")
.declareAcquiredMethod("redirect", "redirect")
.declareAcquiredMethod("getUrlParameter", "getUrlParameter")
/////////////////////////////////////////////////////////////////
// declared methods
/////////////////////////////////////////////////////////////////
.declareMethod('toggle', function () {
.declareMethod('toggle', function toggle() {
return this.changeState({
visible: !this.state.visible
});
})
.declareMethod('close', function () {
.declareMethod('close', function close() {
return this.changeState({
visible: false
});
})
.declareMethod('render', function (options) {
.declareMethod('render', function render(options) {
var erp5_document = options.erp5_document,
jio_key = options.jio_key,
view = options.view,
jump_view = options.jump_view,
visible = options.visible,
extra_menu_list = options.extra_menu_list,
display_workflow_list,
context = this,
workflow_list,
group_mapping,
view_list,
// action_list,
// clone_list,
jump_list;
if (erp5_document !== undefined) {
workflow_list = erp5_document._links.action_workflow || [];
view_list = erp5_document._links.action_object_view || [];
jump_list = erp5_document._links.action_object_jio_jump || [];
if (workflow_list.constructor !== Array) {
workflow_list = [workflow_list];
if (visible === undefined) {
visible = context.state.visible;
}
if (view_list.constructor !== Array) {
view_list = [view_list];
if (options.display_workflow_list === undefined) {
display_workflow_list = true;
} else {
display_workflow_list = asBoolean(options.display_workflow_list);
}
if (jump_list.constructor !== Array) {
jump_list = [jump_list];
if ((erp5_document !== undefined) && (jio_key !== undefined)) {
group_mapping = mergeGlobalActionWithRawActionList(jio_key,
view, jump_view,
erp5_document._links, [
"action_workflow",
"action_object_view", [
"action_object_jio_action",
"action_object_jio_button",
"action_object_jio_fast_input"
],
"action_object_clone_action",
"action_object_jio_jump"
], {
"action_object_jio_action": "display_dialog_with_history",
"action_object_clone_action": "display_dialog_with_history"
}, {
"action_object_clone_action": true
});
workflow_list = JSON.stringify(group_mapping.action_workflow);
view_list = JSON.stringify(group_mapping.action_object_view);
// action_list = JSON.stringify(group_mapping.action_object_jio_action);
// clone_list = JSON.stringify(group_mapping.action_object_clone_action);
jump_list = JSON.stringify(group_mapping.action_object_jio_jump);
}
// Prevent as much as possible to modify the DOM panel
// stateChange prefer to compare strings
workflow_list = JSON.stringify(workflow_list);
view_list = JSON.stringify(view_list);
jump_list = JSON.stringify(jump_list);
if (extra_menu_list !== undefined) {
extra_menu_list = JSON.stringify(extra_menu_list);
}
return this.changeState({
return context.getUrlParameter('editable')
.push(function (editable) {
return context.changeState({
visible: visible,
display_workflow_list: display_workflow_list,
workflow_list: workflow_list,
view_list: view_list,
// action_list: action_list,
// clone_list: clone_list,
jump_list: jump_list,
global: true,
editable: options.editable
jio_key: jio_key,
view: view,
jump_view: jump_view,
editable: false, // asBoolean(options.editable) || asBoolean(editable) || false,
extra_menu_list: extra_menu_list
});
});
})
.onStateChange(function (modification_dict) {
var context = this,
.onStateChange(function onStateChange(modification_dict) {
var i,
gadget = this,
queue = new RSVP.Queue(),
tmp_element;
workflow_list,
view_list,
jump_list,
dl_fragment,
queue = new RSVP.Queue();
if (modification_dict.hasOwnProperty("visible")) {
if (this.state.visible) {
......@@ -101,212 +167,210 @@
if (modification_dict.hasOwnProperty("global")) {
queue
.push(function () {
return RSVP.all([
context.getUrlFor({command: 'display', options: {page: "supportrequest_preference"}}),
context.getUrlFor({command: 'display', options: {page: "logout"}}),
context.getUrlFor({command: 'display_stored_state', options: {jio_key: "support_request_module"}})
]);
})
.push(function (result_list) {
// XXX: Customize panel header!
return context.translateHtml(
panel_template_header() +
panel_template_body({
"preference_href": result_list[0],
"logout_href": result_list[1],
"supportrequest_href": result_list[2]
})
);
})
.push(function (my_translated_or_plain_html) {
tmp_element = document.createElement('div');
tmp_element.innerHTML = my_translated_or_plain_html;
return context.declareGadget('gadget_erp5_searchfield.html', {
scope: "erp5_searchfield",
element: tmp_element.querySelector('[data-gadget-scope="erp5_searchfield"]')
});
return gadget.getDeclaredGadget('erp5_searchfield');
})
.push(function (search_gadget) {
return search_gadget.render({
focus: false
focus: false,
extended_search: ''
});
})
});
}
if (modification_dict.hasOwnProperty("editable")) {
queue
// Update the global links
.push(function () {
context.element.querySelector("div").appendChild(tmp_element);
return context.listenResize();
return RSVP.hash({
url_list: gadget.getUrlForList([
{command: 'display'},
{command: 'display', options: {jio_key: "support_request_module"}},
{command: 'display', options: {page: "supportrequest_preference"}},
{command: 'display', options: {page: "logout"}}
]),
translation_list: gadget.getTranslationList([
'Home',
'Support Requests',
'Preferences',
'Logout'
])
});
})
.push(function (result_dict) {
var element_list = [],
icon_and_key_list = [
'home', null,
'life-ring', null,
'sliders', null,
'power-off', 'o'
];
for (i = 0; i < result_dict.url_list.length; i += 1) {
// <li><a href="URL" class="ui-btn-icon-left ui-icon-ICON" data-i18n="TITLE" accesskey="KEY"></a></li>
element_list.push(domsugar('li', [
domsugar('a', {
href: result_dict.url_list[i],
'class': 'ui-btn-icon-left ui-icon-' + icon_and_key_list[2 * i],
accesskey: icon_and_key_list[2 * i],
text: result_dict.translation_list[i]
})
]));
}
domsugar(gadget.element.querySelector("ul"),
[domsugar(null, element_list)]);
});
}
if ((this.state.global === true) &&
(modification_dict.hasOwnProperty("desktop") ||
modification_dict.hasOwnProperty("editable") ||
(modification_dict.hasOwnProperty("editable") ||
modification_dict.hasOwnProperty("view") ||
modification_dict.hasOwnProperty("jump_view") ||
modification_dict.hasOwnProperty("workflow_list") ||
modification_dict.hasOwnProperty("jump_list") ||
modification_dict.hasOwnProperty("jio_key") ||
modification_dict.hasOwnProperty("view_list") ||
modification_dict.hasOwnProperty("jump_list"))) {
if (!(this.state.desktop && (this.state.view_list !== undefined))) {
queue
.push(function () {
modification_dict.hasOwnProperty("extra_menu_list"))) {
dl_fragment = document.createDocumentFragment();
gadget.element.querySelector("dl").textContent = '';
});
} else {
if (this.state.view_list !== undefined) {
queue
.push(function () {
var i = 0,
promise_list = [],
workflow_list = JSON.parse(gadget.state.workflow_list),
view_list = JSON.parse(gadget.state.view_list),
var parameter_list = [];
view_list = JSON.parse(gadget.state.view_list);
jump_list = JSON.parse(gadget.state.jump_list);
workflow_list = JSON.parse(gadget.state.workflow_list);
for (i = 0; i < workflow_list.length; i += 1) {
promise_list.push(
gadget.getUrlFor({
command: 'change',
options: {
view: workflow_list[i].href,
page: undefined
}
})
);
}
for (i = 0; i < view_list.length; i += 1) {
promise_list.push(
gadget.getUrlFor({
command: 'change',
options: {
view: view_list[i].href,
page: undefined
}
parameter_list = view_list
.concat(workflow_list)
.concat(jump_list)
.map(function (options) {
return options.url_kw;
});
return RSVP.hash({
url_list: gadget.getUrlForList(parameter_list),
translation_dict: gadget.getTranslationDict([
'Views', 'Decisions', 'Jumps'
])
});
})
);
.push(function (result_dict) {
appendDt(dl_fragment, result_dict.translation_dict.Views, 'eye',
view_list, result_dict.url_list, 0);
if (gadget.state.display_workflow_list) {
// show Workflows only on document
appendDt(dl_fragment, result_dict.translation_dict.Decisions, 'cogs',
workflow_list, result_dict.url_list, view_list.length);
}
for (i = 0; i < jump_list.length; i += 1) {
promise_list.push(
gadget.getUrlFor({
command: 'change',
options: {
view: jump_list[i].href,
page: undefined
appendDt(dl_fragment, result_dict.translation_dict.Jumps, 'plane',
jump_list, result_dict.url_list,
view_list.length + workflow_list.length);
});
}
if (gadget.state.hasOwnProperty("extra_menu_list") &&
gadget.state.extra_menu_list) {
queue
.push(function () {
return gadget.getTranslationList(['Global']);
})
);
.push(function (translation_list) {
var extra_menu_list = JSON.parse(gadget.state.extra_menu_list),
href_list = [];
for (i = 0; i < extra_menu_list.length; i += 1) {
href_list.push(extra_menu_list[i].href);
extra_menu_list[i] = {
"class_name": extra_menu_list[i].active ? "active" : "",
"title": extra_menu_list[i].title
};
}
return RSVP.all(promise_list);
})
.push(function (result_list) {
var i,
result_workflow_list = [],
result_view_list = [],
result_jump_list = [],
workflow_list = JSON.parse(gadget.state.workflow_list),
view_list = JSON.parse(gadget.state.view_list),
jump_list = JSON.parse(gadget.state.jump_list);
for (i = 0; i < workflow_list.length; i += 1) {
result_workflow_list.push({
title: workflow_list[i].title,
href: result_list[i]
appendDt(dl_fragment, translation_list[0], 'globe',
extra_menu_list, href_list, 0);
});
}
for (i = 0; i < view_list.length; i += 1) {
result_view_list.push({
title: view_list[i].title,
href: result_list[i + workflow_list.length]
});
}
for (i = 0; i < jump_list.length; i += 1) {
result_jump_list.push({
title: jump_list[i].title,
href: result_list[i + workflow_list.length + view_list.length]
});
queue
.push(function () {
if (dl_fragment) {
domsugar(gadget.element.querySelector("dl"), [dl_fragment]);
}
return gadget.translateHtml(
panel_template_body_desktop({
workflow_list: result_workflow_list,
view_list: result_view_list,
jump_list: result_jump_list
})
).push(function (my_translated_or_plain_html) {
gadget.element.querySelector("dl").innerHTML = my_translated_or_plain_html;
})
});
}
}
return queue;
})
/////////////////////////////////////////////////////////////////
// declared services
/////////////////////////////////////////////////////////////////
.onEvent('click', function (evt) {
.onEvent('click', function click(evt) {
if ((evt.target.nodeType === Node.ELEMENT_NODE) &&
(evt.target.tagName === 'BUTTON')) {
return this.toggle();
}
}, false, false)
.declareJob('listenResize', function () {
// resize should be only trigger after the render method
// as displaying the panel rely on external gadget (for translation for example)
var result,
event,
context = this;
function extractSizeAndDispatch() {
if (window.matchMedia("(min-width: 90em)").matches) {
return context.changeState({
desktop: true
});
}
return context.changeState({
desktop: false
});
}
result = loopEventListener(window, 'resize', false,
extractSizeAndDispatch);
event = document.createEvent("Event");
event.initEvent('resize', true, true);
window.dispatchEvent(event);
return result;
.allowPublicAcquisition("notifyFocus", function notifyFocus() {
// All html5 fields in ERP5JS triggers this method when focus
// is triggered. This is usefull to display error text.
// But, in the case of panel, we don't need to handle anything.
return;
})
.allowPublicAcquisition("notifyBlur", function notifyFocus() {
// All html5 fields in ERP5JS triggers this method when blur
// is triggered now. This is usefull to display error text.
// But, in the case of panel, we don't need to handle anything.
return;
})
.allowPublicAcquisition('notifyChange', function () {
.allowPublicAcquisition('notifyChange', function notifyChange() {
// Typing a search query should not modify the header status
return;
}, {mutex: 'changestate'})
.allowPublicAcquisition('notifyValid', function notifyValid() {
// Typing a search query should not modify the header status
return;
})
.onEvent('submit', function () {
var gadget = this;
return gadget.getDeclaredGadget("erp5_searchfield")
.push(function (search_gadget) {
.onEvent('submit', function submit() {
var gadget = this,
search_gadget,
redirect_options = {
page: "search"
};
return gadget
.getDeclaredGadget("erp5_searchfield")
.push(function (declared_gadget) {
search_gadget = declared_gadget;
return search_gadget.getContent();
})
.push(function (data) {
var options = {
page: "search"
};
if (data.search) {
options.extended_search = '(' + data.search + ' AND portal_type: "Support Request")';
redirect_options.extended_search = '(' + data.search + ' AND portal_type: "Support Request")';
} else {
options.extended_search = '( portal_type: "Support Request")';
redirect_options.extended_search = '( portal_type: "Support Request")';
}
// Remove focus from the search field
document.activeElement.blur();
return gadget.redirect({command: 'display', options: options});
// let the search gadget know its current state (value and focus)
// in order to be able to zero it out in the next Promise
// input gadget's state does not reflect immediate reality
// so we need to manage its state from the parent
return search_gadget.render({
extended_search: data.search,
focus: true
});
})
.push(function () {
// we want the search field in side panel to be empty and blured
return search_gadget.render({
extended_search: '',
focus: false // we don't want focus on the empty field for sure
});
})
.push(function () {
return gadget.redirect({command: 'store_and_display', options: redirect_options}, true);
});
}, false, true)
.onEvent('click', function (evt) {
if ((evt.target.nodeType === Node.ELEMENT_NODE) &&
(evt.target.tagName === 'BUTTON')) {
return this.toggle();
}
}, false, false)
.onEvent('blur', function (evt) {
// XXX Horrible hack to clear the search when focus is lost
// This does not follow renderJS design, as a gadget should not touch
// another gadget content
if (evt.target.type === 'search') {
evt.target.value = "";
}
}, true, false);
}, /*useCapture=*/false, /*preventDefault=*/true);
}(window, document, rJS, Handlebars, RSVP, Node, rJS.loopEventListener));
}(window, document, rJS, RSVP, Node, asBoolean, ensureArray,
mergeGlobalActionWithRawActionList, domsugar));
\ No newline at end of file
......@@ -262,7 +262,7 @@
</tuple>
<state>
<tuple>
<float>1625467130.12</float>
<float>1694147830.81</float>
<string>UTC</string>
</tuple>
</state>
......
......@@ -11,47 +11,9 @@ hr#post_item {
margin-bottom: 20px;
}
#post_list li ol {
list-style: initial;
list-style-type: decimal;
padding-left: 2em;
}
#post_list li ul {
list-style: initial;
padding-left: 2em;
}
#post_list li p {
margin-block-start: 1em;
margin-block-end: 1em;
}
#post_list li h1 {
font-size: 2em;
font-weight: bold;
}
#post_list li h2 {
font-size: 1.5em;
font-weight: bold;
}
#post_list li h3 {
font-size: 1.17em;
font-weight: bold;
}
#post_list li h4 {
font-weight: bold;
}
#post_list li h5 {
font-size: 0.83em;
font-weight: bold;
}
#post_list li h6 {
font-size: 0.67em;
font-weight: bold;
.comments-header {
background-color:#0E81C2;
color:white;
margin:1em 0;
padding:0.5em;
}
......@@ -268,8 +268,8 @@
</tuple>
<state>
<tuple>
<float>1542361236.16</float>
<string>GMT+9</string>
<float>1694060317.8</float>
<string>UTC</string>
</tuple>
</state>
</object>
......
<!DOCTYPE html>
<html>
<!--
data-i18n="Posting comment"
data-i18n="Post content can not be empty!"
data-i18n="Comment added"
data-i18n="No comment yet."
data-i18n="By"
data-i18n="Attachment:"
-->
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width" />
<title>ERP5 PT Form View</title>
<!-- renderjs -->
<script src="rsvp.js" type="text/javascript"></script>
<script src="renderjs.js" type="text/javascript"></script>
<script src="rsvp.js"></script>
<script src="renderjs.js"></script>
<!-- custom script -->
<script src="handlebars.js" type="text/javascript"></script>
<script src="gadget_global.js" type="text/javascript"></script>
<script src="jiodev.js" type="text/javascript"></script>
<script src="gadget_erp5_global.js" type="text/javascript"></script>
<script src="gadget_erp5_pt_form_view_discussable.js" type="text/javascript"></script>
<script src="domsugar.js"></script>
<script src="gadget_global.js"></script>
<script src="jiodev.js"></script>
<script src="gadget_erp5_global.js"></script>
<script src="gadget_erp5_pt_form_view_discussable.js"></script>
<link rel="stylesheet" type="text/css" href="gadget_erp5_pt_form_view_discussable.css">
<!-- templates -->
<script id="template-document-list" type="text/x-handlebars-template">
{{#if comments }}
{{#each comments }}
<li>By <strong>{{ user }}</strong> -
<time datetime="{{ date }}" title="{{ date_formatted }}">{{ date_relative }}</time>
<br/>
{{{ text }}}
{{#if attachment_link }}
<br/>
<strong>Attachment: </strong>
<a href="{{attachment_link}}">{{ attachment_name }}</a>
{{/if}}
<hr id="post_item">
</li>
{{/each }}
{{else }}
<p><em>No comment yet.</em></p><hr id="post_item">
{{/if }}
</script>
</head>
<body>
<!-- XXX this is a form replacement -->
......@@ -49,18 +36,21 @@
</div>
<form>
<p style="background-color:#0E81C2;color:white;margin:1em 0;padding:0.5em">Comments:</p>
<div>
<p class="comments-header" data-i18n="Comments:">Comments:</p>
<ol id="post_list"></ol>
<h3 class="ui-content-title ui-body-c ui-icon ui-icon-custom ui-icon-random" id="comment-title" name="comment-title">&nbsp;Post a comment</h3>
<h3 class="ui-content-title ui-body-c ui-icon ui-icon-custom ui-icon-random" id="comment-title" name="comment-title" data-i18n="Post Comment">&nbsp;Post a comment</h3>
</div>
<div data-gadget-url="gadget_editor.html"
data-gadget-scope="editor"
data-gadget-sandbox="">
</div>
<div id="file_upload_div">
<input value="" name="attachment" id="attachment" type="file" title="Upload">
</div>
<div>
<input data-theme="b" data-inline="true" type="submit" data-i18n="[value]Post Comment" value="Post Comment" data-icon="check" disabled class="ui-disabled"/>
</div>
</form>
</div>
</body>
......
......@@ -240,7 +240,7 @@
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>974.28140.2414.14967</string> </value>
<value> <string>1010.62469.29459.39441</string> </value>
</item>
<item>
<key> <string>state</string> </key>
......@@ -260,7 +260,7 @@
</tuple>
<state>
<tuple>
<float>1554190620.27</float>
<float>1694063872.02</float>
<string>UTC</string>
</tuple>
</state>
......
/*global window, rJS, RSVP, calculatePageTitle, FormData, URI, jIO, moment, Handlebars */
/*global window, rJS, RSVP, calculatePageTitle, FormData, URI, jIO, moment */
/*jslint nomen: true, indent: 2, maxerr: 3 */
(function (window, rJS, RSVP, calculatePageTitle, moment, Handlebars) {
(function (window, rJS, RSVP, calculatePageTitle, moment) {
"use strict";
var gadget_klass = rJS(window),
comment_list_template = Handlebars.compile(
gadget_klass.__template_element.getElementById("template-document-list").innerHTML
);
gadget_klass
/**
* french locale for momentjs, copied from https://momentjs.com/docs/
*/
moment.locale('fr', {
months : 'janvier_février_mars_avril_mai_juin_juillet_août_septembre_octobre_novembre_décembre'.split('_'),
monthsShort : 'janv._févr._mars_avr._mai_juin_juil._août_sept._oct._nov._déc.'.split('_'),
monthsParseExact : true,
weekdays : 'dimanche_lundi_mardi_mercredi_jeudi_vendredi_samedi'.split('_'),
weekdaysShort : 'dim._lun._mar._mer._jeu._ven._sam.'.split('_'),
weekdaysMin : 'Di_Lu_Ma_Me_Je_Ve_Sa'.split('_'),
weekdaysParseExact : true,
longDateFormat : {
LT : 'HH:mm',
LTS : 'HH:mm:ss',
L : 'DD/MM/YYYY',
LL : 'D MMMM YYYY',
LLL : 'D MMMM YYYY HH:mm',
LLLL : 'dddd D MMMM YYYY HH:mm'
},
calendar : {
sameDay : '[Aujourd’hui à] LT',
nextDay : '[Demain à] LT',
nextWeek : 'dddd [à] LT',
lastDay : '[Hier à] LT',
lastWeek : 'dddd [dernier à] LT',
sameElse : 'L'
},
relativeTime : {
future : 'dans %s',
past : 'il y a %s',
s : 'quelques secondes',
m : 'une minute',
mm : '%d minutes',
h : 'une heure',
hh : '%d heures',
d : 'un jour',
dd : '%d jours',
M : 'un mois',
MM : '%d mois',
y : 'un an',
yy : '%d ans'
},
dayOfMonthOrdinalParse : /\d{1,2}(er|e)/,
ordinal : function (number) {
return number + (number === 1 ? 'er' : 'e');
},
meridiemParse : /PD|MD/,
isPM : function (input) {
return input.charAt(0) === 'M';
},
// In case the meridiem units are not separated around 12, then implement
// this function (look at locale/id.js for an example).
// meridiemHour : function (hour, meridiem) {
// return /* 0-23 hour, given meridiem token and hour 1-12 */ ;
// },
meridiem : function (hours, minutes, isLower) {
return hours < 12 ? 'PD' : 'MD';
},
week : {
dow : 1, // Monday is the first day of the week.
doy : 4 // Used to determine first week of the year.
}
});
rJS(window)
/////////////////////////////////////////////////////////////////
// Acquired methods
/////////////////////////////////////////////////////////////////
.declareAcquiredMethod("updateHeader", "updateHeader")
.declareAcquiredMethod("translate", "translate")
.declareAcquiredMethod("translateHtml", "translateHtml")
.declareAcquiredMethod("getTranslationList", "getTranslationList")
.declareAcquiredMethod("getSetting", "getSetting")
.declareAcquiredMethod("getSettingList", "getSettingList")
.declareAcquiredMethod("jio_getAttachment", "jio_getAttachment")
......@@ -125,7 +189,19 @@
maximize: true
})]);
}
).push(function () {
)
.push(function() {
return gadget.getTranslationList([
"Comments:",
"Post Comment",
"Post Comment",
]).push(function(translation_list) {
gadget.element.querySelector("[data-i18n='Comments:']").innerText = translation_list[0];
gadget.element.querySelector("[data-i18n='Post Comment']").innerText = "\u00A0" + translation_list[1];
gadget.element.querySelector("[data-i18n='[value]Post Comment']").value = translation_list[2];
});
})
.push(function () {
// make our submit button editable
var element = gadget.element.querySelector('input[type="submit"]');
element.removeAttribute('disabled');
......@@ -169,12 +245,19 @@
});
})
.push(function () {
return gadget.jio_getAttachment(
return RSVP.all([
gadget.jio_getAttachment(
'post_module',
gadget.hateoas_url + gadget.options.jio_key + "/SupportRequest_getCommentPostListAsJson"
);
),
gadget.getTranslationList(["By", "Attachment:",])
]);
})
.push(function (post_list) {
.push(
function (post_list_and_translation_list) {
var post_list = post_list_and_translation_list[0],
translationBy = post_list_and_translation_list[1][0],
translationAttachment = post_list_and_translation_list[1][1];
function getPostWithLinkAndLocalDate(post) {
post.date_formatted = moment(post.date).format('LLLL');
post.date_relative = moment(post.date).fromNow();
......@@ -188,16 +271,67 @@
}
);
}
// build links with attachments and localized dates
var queue_list = [], i = 0;
for (i = 0; i < post_list.length; i += 1) {
queue_list.push(getPostWithLinkAndLocalDate(post_list[i]));
return RSVP.all(post_list.map(getPostWithLinkAndLocalDate))
.then(function(post_list){
function getPostDomList(post) {
var dom_list = [
translationBy + " ", // XXX translations can not have leading space ?
domsugar("strong", [post.user]),
" - ",
domsugar("time", {
datetime: post.date,
title: post.date_formatted
},
[post.date_relative]
),
domsugar("br"),
// the post content is set as an attribute for now, we'll use a
// gadget_html_viewer to render each post
domsugar("div", {
'data-gadget-html-viewer-value': post.text,
})
];
if (post.attachment_link) {
dom_list.push(domsugar("br"))
dom_list.push(domsugar("strong", [translationAttachment]))
dom_list.push(domsugar("a", { href: post.attachment_link }, [post.attachment_name]))
}
return RSVP.all(queue_list);
return [
domsugar("li", dom_list),
domsugar("hr", { id: "post_item" })
];
}
return post_list.map(getPostDomList)
});
})
.push(function (comment_list) {
var comments = gadget.element.querySelector("#post_list");
comments.innerHTML = comment_list_template({comments: comment_list});
.push(function(dom_list) {
return gadget.getElement()
.push(function (element) {
var all_dom_list = [], gadget_list = [], element_list, i, element;
// add to DOM all the posts, with data-gadget-html-viewer-value attribute
for (var i = 0; i < dom_list.length; i += 1) {
all_dom_list = all_dom_list.concat(dom_list[i]);
}
domsugar(element.querySelector("#post_list"), all_dom_list);
// make gadget html viewer for each post
element_list = element.querySelector("#post_list").querySelectorAll('[data-gadget-html-viewer-value]');
for (i = 0; i < element_list.length; i += 1) {
gadget_list.push(
gadget.declareGadget("gadget_html_viewer.html", {
element: element_list[i],
scope: "html_viewer",
sandbox: "public"
})
.push(function (g) {
return g.render({ value: g.element.getAttribute("data-gadget-html-viewer-value") });
})
);
}
return RSVP.all(gadget_list);
});
});
})
.declareJob('submitPostComment', function () {
......@@ -209,9 +343,12 @@
.push(function (e) {
return e.getContent();
})
.push(function (content) {
.push(function (content) {
if (content.comment === '') {
return gadget.notifySubmitted({message: "Post content can not be empty!"});
return gadget.translate("Post content can not be empty!")
.push(function (translated_message) {
return gadget.notifySubmitted({message: translated_message});
})
}
submitButton = gadget.element.querySelector("input[type=submit]");
......@@ -222,7 +359,10 @@
submitButton.disabled = false;
submitButton.classList.remove("ui-disabled");
}
queue = gadget.notifySubmitted({message: "Posting comment"})
queue = gadget.translate("Posting comment").
push(function (message_posting_comment) {
return gadget.notifySubmitted({message: message_posting_comment})
})
.push(function () {
var choose_file_html_element = gadget.element.querySelector('#attachment'),
file_blob = choose_file_html_element.files[0],
......@@ -250,8 +390,12 @@
});
})
.push(function () {
return new RSVP.Queue().push(function () {
gadget.notifySubmitted({message: "Comment added", status: "success"});
return new RSVP.Queue().push(
function(){
return gadget.translate("Comment added")
}
).push(function (message_comment_added) {
gadget.notifySubmitted({message: message_comment_added, status: "success"});
}).push(function () {
return gadget.redirect({command: 'reload'});
});
......@@ -273,4 +417,4 @@
.onEvent('submit', function () {
return this.submitPostComment();
});
}(window, rJS, RSVP, calculatePageTitle, moment, Handlebars));
}(window, rJS, RSVP, calculatePageTitle, moment));
......@@ -240,7 +240,7 @@
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>985.17756.63769.9284</string> </value>
<value> <string>1010.63187.6525.24985</string> </value>
</item>
<item>
<key> <string>state</string> </key>
......@@ -260,7 +260,7 @@
</tuple>
<state>
<tuple>
<float>1625719287.68</float>
<float>1694063993.84</float>
<string>UTC</string>
</tuple>
</state>
......
......@@ -366,7 +366,7 @@
</item>
<item>
<key> <string>configuration_content_security_policy</string> </key>
<value> <string>default-src \'self\'; img-src \'self\' data:; media-src \'self\' blob:; connect-src \'self\' data:; script-src \'self\' \'unsafe-eval\'; font-src \'self\'; style-src \'self\' \'unsafe-inline\' data:; frame-src \'self\' data:</string> </value>
<value> <string>default-src \'self\'; img-src \'self\' data:; media-src \'self\' blob:; connect-src \'self\' data:; script-src \'self\'; font-src \'self\'; style-src \'self\' data:; frame-src \'self\' data:</string> </value>
</item>
<item>
<key> <string>configuration_default_view_action_reference</string> </key>
......
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Web Section" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Add_portal_content_Permission</string> </key>
<value>
<tuple>
<string>Assignor</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>_Add_portal_folders_Permission</string> </key>
<value>
<tuple>
<string>Assignor</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>_Copy_or_Move_Permission</string> </key>
<value>
<tuple>
<string>Assignor</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>_Delete_objects_Permission</string> </key>
<value>
<tuple>
<string>Assignor</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>_Modify_portal_content_Permission</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Manager</string>
<string>Owner</string>
</tuple>
</value>
</item>
<item>
<key> <string>__before_publishing_traverse__</string> </key>
<value>
<object>
<klass>
<global name="_reconstructor" module="copy_reg"/>
</klass>
<tuple>
<global name="MultiHook" module="ZPublisher.BeforeTraverse"/>
<global name="object" module="__builtin__"/>
<none/>
</tuple>
<state>
<dictionary>
<item>
<key> <string>_defined_in_class</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>_hookname</string> </key>
<value> <string>__before_publishing_traverse__</string> </value>
</item>
<item>
<key> <string>_list</string> </key>
<value>
<list>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</list>
</value>
</item>
<item>
<key> <string>_prior</string> </key>
<value>
<none/>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>__before_traverse__</string> </key>
<value>
<dictionary>
<item>
<key>
<tuple>
<int>99</int>
<string>ERP5 Web Section/unsafe</string>
</tuple>
</key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>_identity_criterion</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
<item>
<key> <string>_local_properties</string> </key>
<value>
<tuple>
<dictionary>
<item>
<key> <string>id</string> </key>
<value> <string>configuration_content_security_policy</string> </value>
</item>
<item>
<key> <string>type</string> </key>
<value> <string>string</string> </value>
</item>
</dictionary>
<dictionary>
<item>
<key> <string>id</string> </key>
<value> <string>configuration_x_frame_options</string> </value>
</item>
<item>
<key> <string>type</string> </key>
<value> <string>string</string> </value>
</item>
</dictionary>
</tuple>
</value>
</item>
<item>
<key> <string>_range_criterion</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAQ=</string> </persistent>
</value>
</item>
<item>
<key> <string>configuration_content_security_policy</string> </key>
<value> <string>default-src \'self\' data: blob: ; script-src \'self\' \'unsafe-eval\'; style-src \'self\' \'unsafe-inline\'</string> </value>
</item>
<item>
<key> <string>configuration_x_frame_options</string> </key>
<value> <string>SAMEORIGIN</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>empty_criterion_valid</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>unsafe</string> </value>
</item>
<item>
<key> <string>int_index</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>membership_criterion_category</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Web Section</string> </value>
</item>
<item>
<key> <string>short_title</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>string_index</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>test_method_id</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Unsafe</string> </value>
</item>
<item>
<key> <string>visible</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>workflow_history</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAU=</string> </persistent>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="WebSectionTraversalHook" module="Products.ERP5.Document.WebSection"/>
</pickle>
<pickle>
<dictionary/>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary/>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="4" aka="AAAAAAAAAAQ=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary/>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="5" aka="AAAAAAAAAAU=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary>
<item>
<key> <string>category_publication_workflow</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAY=</string> </persistent>
</value>
</item>
<item>
<key> <string>edit_workflow</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAc=</string> </persistent>
</value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="6" aka="AAAAAAAAAAY=">
<pickle>
<global name="WorkflowHistoryList" module="Products.ERP5Type.Workflow"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_log</string> </key>
<value>
<list>
<dictionary>
<item>
<key> <string>action</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>actor</string> </key>
<value> <string>zope</string> </value>
</item>
<item>
<key> <string>comment</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>error_message</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>time</string> </key>
<value>
<object>
<klass>
<global name="_reconstructor" module="copy_reg"/>
</klass>
<tuple>
<global name="DateTime" module="DateTime.DateTime"/>
<global name="object" module="__builtin__"/>
<none/>
</tuple>
<state>
<tuple>
<float>1501748238.58</float>
<string>UTC</string>
</tuple>
</state>
</object>
</value>
</item>
<item>
<key> <string>validation_state</string> </key>
<value> <string>embedded</string> </value>
</item>
</dictionary>
</list>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="7" aka="AAAAAAAAAAc=">
<pickle>
<global name="WorkflowHistoryList" module="Products.ERP5Type.Workflow"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_log</string> </key>
<value>
<list>
<dictionary>
<item>
<key> <string>action</string> </key>
<value> <string>edit</string> </value>
</item>
<item>
<key> <string>actor</string> </key>
<value> <string>zope</string> </value>
</item>
<item>
<key> <string>comment</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>error_message</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>961.50869.49076.30293</string> </value>
</item>
<item>
<key> <string>state</string> </key>
<value> <string>current</string> </value>
</item>
<item>
<key> <string>time</string> </key>
<value>
<object>
<klass>
<global name="_reconstructor" module="copy_reg"/>
</klass>
<tuple>
<global name="DateTime" module="DateTime.DateTime"/>
<global name="object" module="__builtin__"/>
<none/>
</tuple>
<state>
<tuple>
<float>1504539105.96</float>
<string>UTC</string>
</tuple>
</state>
</object>
</value>
</item>
</dictionary>
</list>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
......@@ -11,9 +11,6 @@ workflow = portal.portal_workflow.ticket_workflow
for state in workflow.getStateValueList():
state_title = state.title_or_id()
if 0:
# We don't translate yet, it needs several other fixes
# see https://lab.nexedi.com/nexedi/erp5/merge_requests/778
state_title = unicode(translateString(
'%s [state in %s]' % (state_title, workflow.getId()),
default=unicode(translateString(state_title))))
......
url_list = [
'handlebars.js',
'gadget_supportrequest_header.html',
'gadget_supportrequest_header.js',
'gadget_erp5_page_homepage.css',
......@@ -14,19 +13,15 @@ url_list = [
'gadget_erp5_pt_form_view_discussable.css',
'gadget_erp5_pt_form_view_discussable.html',
'gadget_erp5_pt_form_view_discussable.js',
'gadget_field_graph_echarts.html',
'gadget_field_graph_echarts.js',
'gadget_supportrequest_page_worklist.html',
'gadget_supportrequest_page_worklist.js',
'gadget_supportrequest_panel.html',
'gadget_supportrequest_panel.js',
# Echarts. Should probably be a separated script
'unsafe/gadget_field_graph_echarts.html/',
'unsafe/gadget_field_graph_echarts.html/rsvp.js',
'unsafe/gadget_field_graph_echarts.html/renderjs.js',
'unsafe/gadget_field_graph_echarts.html/echarts-all.js',
'unsafe/gadget_field_graph_echarts.html/gadget_global.js',
'unsafe/gadget_field_graph_echarts.html/unsafe/gadget_field_graph_echarts.js',
# Echarts. Could probably be a separated script
'gadget_field_graph_echarts.html',
'gadget_field_graph_echarts.css',
'gadget_field_graph_echarts.js',
'echarts-all.js',
]
return url_list
......@@ -71,7 +71,7 @@
</tr>
<tr>
<td>waitForText</td>
<td>//ol[@id="post_list"]//li/p</td>
<td>//ol[@id="post_list"]//li//p</td>
<td>Post test</td>
</tr>
<tr>
......
......@@ -90,7 +90,7 @@
</tr>
<tr>
<td>waitForText</td>
<td>//ol[@id="post_list"]//li/p</td>
<td>//ol[@id="post_list"]//li//p</td>
<td>Post test</td>
</tr>
<tr>
......@@ -207,12 +207,12 @@
</tal:block>
<tr>
<td>waitForElementPresent</td>
<td>//a[@data-i18n="Home"]</td>
<td>//a[contains(@class, "ui-icon-home") and text() = "Home"]</td>
<td></td>
</tr>
<tr>
<td>click</td>
<td>//a[@data-i18n="Home"]</td>
<td>//a[contains(@class, "ui-icon-home") and text() = "Home"]</td>
<td></td>
</tr>
<tr>
......@@ -324,12 +324,12 @@
</tr>
<tr>
<td>waitForElementPresent</td>
<td>//div[@data-gadget-scope="panel"]//a[@data-i18n="Documents"]</td>
<td>//div[@data-gadget-scope="panel"]//a[text() = "Documents"]</td>
<td></td>
</tr>
<tr>
<td>click</td>
<td>//div[@data-gadget-scope="panel"]//a[@data-i18n="Documents"]</td>
<td>//div[@data-gadget-scope="panel"]//a[text() = "Documents"]</td>
<td></td>
</tr>
<tal:block tal:define="notification_configuration python: {'class': 'success',
......
......@@ -56,12 +56,7 @@
<tr>
<td>waitForText</td>
<td>//ol[@id="post_list"]//li[1]/p</td>
<td>Post test 1</td>
</tr>
<tr>
<td>waitForText</td>
<td>//ol[@id="post_list"]//li[1]/p</td>
<td>//ol[@id="post_list"]//li[1]//p</td>
<td>Post test 1</td>
</tr>
<tr>
......@@ -115,12 +110,12 @@ displayed
</tr>
<tr>
<td>waitForText</td>
<td>//ol[@id="post_list"]//li[2]/p</td>
<td>//ol[@id="post_list"]//li[2]//p</td>
<td>Post test 2</td>
</tr>
<tr>
<td>assertText</td>
<td>//ol[@id="post_list"]//li[1]/p</td>
<td>//ol[@id="post_list"]//li[1]//p</td>
<td>Post test 1</td>
</tr>
<tr>
......@@ -171,17 +166,17 @@ post ingested when submitting a new support request.
</tr>
<tr>
<td>waitForText</td>
<td>//ol[@id="post_list"]//li[3]/p</td>
<td>//ol[@id="post_list"]//li[3]//p</td>
<td>Post test 3</td>
</tr>
<tr>
<td>assertText</td>
<td>//ol[@id="post_list"]//li[1]/p</td>
<td>//ol[@id="post_list"]//li[1]//p</td>
<td>Post test 1</td>
</tr>
<tr>
<td>assertText</td>
<td>//ol[@id="post_list"]//li[2]/p</td>
<td>//ol[@id="post_list"]//li[2]//p</td>
<td>Post test 2</td>
</tr>
......@@ -252,7 +247,7 @@ and check that the relative time was updated.
</tr>
<tr>
<td>waitForText</td>
<td>//ol[@id="post_list"]//li[3]/p</td>
<td>//ol[@id="post_list"]//li[3]//p</td>
<td>Post test 4</td>
</tr>
</tbody></table>
......
......@@ -56,7 +56,7 @@
<tr>
<td>waitForText</td>
<td>//ol[@id="post_list"]//li[1]/p</td>
<td>//ol[@id="post_list"]//li[1]//p</td>
<td>First Post Content</td>
</tr>
......@@ -136,18 +136,18 @@
<!-- The second post was submitted only once. -->
<tr>
<td>waitForText</td>
<td>//ol[@id="post_list"]//li[1]/p</td>
<td>//ol[@id="post_list"]//li[1]//p</td>
<td>First Post Content</td>
</tr>
<tr>
<td>waitForText</td>
<td>//ol[@id="post_list"]//li[2]/p</td>
<td>//ol[@id="post_list"]//li[2]//p</td>
<td>Second Post Content</td>
</tr>
<tr>
<td>assertElementNotPresent</td>
<td>//ol[@id="post_list"]//li[3]/p</td>
<td>//ol[@id="post_list"]//li[3]//p</td>
<td></td>
</tr>
......
......@@ -28,7 +28,7 @@
</item>
<item>
<key> <string>width</string> </key>
<value> <int>390</int> </value>
<value> <int>488</int> </value>
</item>
</dictionary>
</pickle>
......
......@@ -64,7 +64,7 @@
<tr><td colspan="3"><b>Verify the rendering of the graph matches our reference snapshot</b></td></tr>
<tr><td>verifyImageMatchSnapshot</td>
<td>//canvas</td>
<td>20</td></tr>
<td>0</td></tr>
<tr><td colspan="3"><b>Clicking on a serie filter the listbox of recent updates</b></td></tr>
......
......@@ -28,7 +28,7 @@
</item>
<item>
<key> <string>width</string> </key>
<value> <int>404</int> </value>
<value> <int>503</int> </value>
</item>
</dictionary>
</pickle>
......
......@@ -81,7 +81,7 @@
<tr><td colspan="3"><b>Verify the rendering of the graph matches our reference snapshot</b></td></tr>
<tr><td>verifyImageMatchSnapshot</td>
<td>//canvas</td>
<td>20</td></tr>
<td>0</td></tr>
<tr><td colspan="3"><b>Clicking on a serie filter the listbox of recent updates</b></td></tr>
......
......@@ -26,17 +26,12 @@
</tr>
<tr>
<td>waitForElementPresent</td>
<td>//a[@data-i18n="Support Requests"]</td>
<td></td>
</tr>
<tr>
<td>assertElementPresent</td>
<td>//a[@data-i18n="Support Requests"]</td>
<td>//a[contains(@class, "ui-icon-life-ring") and text() = "Support Requests"]</td>
<td></td>
</tr>
<tr>
<td>click</td>
<td>//a[@data-i18n="Support Requests"]</td>
<td>//a[contains(@class, "ui-icon-life-ring") and text() = "Support Requests"]</td>
<td></td>
</tr>
<tr>
......
......@@ -72,10 +72,10 @@
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/wait_for_content_loaded" />
<tr><td>waitForText</td>
<td>//ol[@id="post_list"]//li/p</td>
<td>//ol[@id="post_list"]//li//p</td>
<td>Post test</td></tr>
<tr><td>assertText</td>
<td>//ol[@id="post_list"]//li/p</td>
<td>//ol[@id="post_list"]//li//p</td>
<td>Post test</td></tr>
<tr><td colspan="3"><b>Go back ot front page and check "latest post" columns</b></td></tr>
......
......@@ -18,13 +18,13 @@
<tr>
<td>click</td>
<td>//a[@data-i18n='Support Requests']</td>
<td>//a[contains(@class, "ui-icon-life-ring") and text() = "Support Requests"]</td>
<td></td>
</tr>
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/wait_for_content_loaded" />
<tr>
<td>assertElementPresent</td>
<td>//a[@data-i18n='Support Requests']</td>
<td>//a[contains(@class, "ui-icon-life-ring") and text() = "Support Requests"]</td>
<td></td>
</tr>
<tr>
......
......@@ -16,17 +16,12 @@
</tr>
<tr>
<td>waitForElementPresent</td>
<td>//a[@data-i18n="Support Requests"]</td>
<td></td>
</tr>
<tr>
<td>assertElementPresent</td>
<td>//a[@data-i18n="Support Requests"]</td>
<td>//a[contains(@class, "ui-icon-life-ring") and text() = "Support Requests"]</td>
<td></td>
</tr>
<tr>
<td>click</td>
<td>//a[@data-i18n="Support Requests"]</td>
<td>//a[contains(@class, "ui-icon-life-ring") and text() = "Support Requests"]</td>
<td></td>
</tr>
<tr>
......
......@@ -21,10 +21,9 @@
<tr>
<td>assertElementPresent</td>
<td>//h1[@data-i18n="[value]Support Request WorkLists"]</td>
<td>//h1[@data-i18n="Support Request WorkLists"]</td>
<td></td>
</tr>
<tr>
<td>assertElementPresent</td>
<td>//section[@class="document_list"]//li[1]</td>
......
......@@ -54,7 +54,7 @@
</item>
<item>
<key> <string>title</string> </key>
<value> <unicode></unicode> </value>
<value> <unicode>Home Page Listbox Translation</unicode> </value>
</item>
</dictionary>
</pickle>
......
<html>
<head>
<title tal:content="template/title">The title</title>
<title tal:content="template/title_and_id"></title>
<meta http-equiv="content-type" content="text/html;charset=utf-8">
</head>
<body>
<table cellpadding="1" cellspacing="1" border="1">
<thead>
<tr><td rowspan="1" colspan="3">Home Page Listbox Translation</td></tr>
<tr><td rowspan="1" colspan="3" tal:content="template/title_and_id"></td></tr>
</thead><tbody>
<tal:block metal:use-macro="here/Zuite_SupportRequestUITemplate/macros/init" />
<tal:block metal:use-macro="here/Zuite_SupportRequestUITemplate/macros/cleanup_module" />
......@@ -24,11 +24,6 @@
<td>//input[@data-i18n='[value]Submit New Support Request']</td>
<td></td>
</tr>
<tr>
<td>assertElementPresent</td>
<td>//input[@data-i18n='[value]Submit New Support Request']</td>
<td></td>
</tr>
<tr>
<td>waitForElementPresent</td>
<td>//div[@class="document_table"]//tr/td[1]</td>
......
<html>
<head>
<title tal:content="template/title">The title</title>
<meta http-equiv="content-type" content="text/html;charset=utf-8">
</head>
<body>
<table cellpadding="1" cellspacing="1" border="1">
<thead>
<tr><td rowspan="1" colspan="3">Home Page Panel Translation</td></tr>
</thead><tbody>
<tal:block metal:use-macro="here/Zuite_SupportRequestUITemplate/macros/init" />
<tr>
<td>open</td>
<td>${base_url}/web_site_module/erp5_officejs_support_request_ui/fr/</td>
<td></td>
</tr>
<!-- hack to display Views&Decisions on panel for small screen-->
<tr>
<td>getEval</td>
<td>window.matchMedia = function () {return {matches:true}}</td>
<td></td>
</tr>
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/wait_for_app_loaded" />
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/wait_for_listbox_loaded" />
<tr>
<td>waitForElementPresent</td>
<td>//input[@data-i18n='[value]Submit New Support Request']</td>
<td></td>
</tr>
<tr>
<td>assertElementPresent</td>
<td>//input[@data-i18n='[value]Submit New Support Request']</td>
<td></td>
</tr>
<tr>
<td>waitForElementPresent</td>
<td>//div[contains(@class, "panel")]//a</td>
<td></td>
</tr>
<tr>
<td>assertText</td>
<td>//div[contains(@class, "panel")]//a[contains(@class, "ui-icon-home")]</td>
<td>Accueil</td>
</tr>
<tr>
<td>assertText</td>
<td>//div[contains(@class, "panel")]//a[contains(@class, "ui-icon-life-ring")]</td>
<td>Demandes d'assistance</td>
</tr>
<tr>
<td>assertText</td>
<td>//div[contains(@class, "panel")]//a[contains(@class, "ui-icon-sliders")]</td>
<td>Préférences</td>
</tr>
<tr>
<td>assertText</td>
<td>//div[contains(@class, "panel")]//a[contains(@class, "ui-icon-power-off")]</td>
<td>Déconnexion</td>
</tr>
<tr>
<td>waitForElementPresent</td>
<td>//div[contains(@class, "panel")]//dt[contains(@class, "ui-icon-eye")]</td>
<td></td>
</tr>
<tr>
<td>assertText</td>
<td>//div[contains(@class, "panel")]//dt[contains(@class, "ui-icon-eye")]</td>
<td>Vues</td>
</tr>
<tr>
<td>assertText</td>
<td>//div[contains(@class, "panel")]//dt[contains(@class, "ui-icon-cogs")]</td>
<td>Décisions</td>
</tr>
</tbody></table>
</body>
</html>
\ No newline at end of file
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Image" module="OFS.Image"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>__name__</string> </key>
<value> <string>testHomePageTranslation-reference-snapshot-1.png</string> </value>
</item>
<item>
<key> <string>content_type</string> </key>
<value> <string>image/png</string> </value>
</item>
<item>
<key> <string>height</string> </key>
<value> <int>285</int> </value>
</item>
<item>
<key> <string>precondition</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>width</string> </key>
<value> <int>503</int> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Image" module="OFS.Image"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>__name__</string> </key>
<value> <string>testHomePageTranslation-reference-snapshot-2.png</string> </value>
</item>
<item>
<key> <string>content_type</string> </key>
<value> <string>image/png</string> </value>
</item>
<item>
<key> <string>height</string> </key>
<value> <int>285</int> </value>
</item>
<item>
<key> <string>precondition</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>width</string> </key>
<value> <int>488</int> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
......@@ -46,7 +46,7 @@
</item>
<item>
<key> <string>id</string> </key>
<value> <string>testHomePagePanelTranslation</string> </value>
<value> <string>testHomePageTranslation</string> </value>
</item>
<item>
<key> <string>output_encoding</string> </key>
......@@ -54,7 +54,7 @@
</item>
<item>
<key> <string>title</string> </key>
<value> <unicode></unicode> </value>
<value> <unicode>Home Page Translation</unicode> </value>
</item>
</dictionary>
</pickle>
......
<html>
<head>
<title tal:content="template/title_and_id"></title>
<meta http-equiv="content-type" content="text/html;charset=utf-8">
</head>
<body>
<table cellpadding="1" cellspacing="1" border="1">
<thead>
<tr>
<td rowspan="1" colspan="3" tal:content="template/title_and_id"></td>
</tr>
</thead>
<tbody>
<tal:block metal:use-macro="here/Zuite_SupportRequestUITemplate/macros/init" />
<tr>
<td>open</td>
<td>${base_url}/web_site_module/erp5_officejs_support_request_ui/fr/</td>
<td></td>
</tr>
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/wait_for_app_loaded" />
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/wait_for_listbox_loaded" />
<tr>
<td>waitForElementPresent</td>
<td>//input[@data-i18n='[value]Submit New Support Request']</td>
<td></td>
</tr>
<!-- check graphs match snapshots -->
<tr>
<td>waitForElementPresent</td>
<td>css=#wrap1 iframe</td>
<td></td>
</tr>
<tr>
<td>selectFrame</td>
<td>css=#wrap1 iframe</td>
<td></td>
</tr>
<tr>
<td>waitForElementPresent</td>
<td>//canvas</td>
<td></td>
</tr>
<tr>
<td>waitForElementPresent</td>
<td>//div[@class="graph-content" and not(@disabled)]</td>
<td></td>
</tr>
<tr>
<td>verifyImageMatchSnapshot</td>
<td>//canvas</td>
<td>0</td>
</tr>
<!-- TODO: click and check listbox -->
<tr>
<td>selectFrame</td>
<td>relative=top</td>
<td></td>
</tr>
<tr>
<td>waitForElementPresent</td>
<td>css=#wrap2 iframe</td>
<td></td>
</tr>
<tr>
<td>selectFrame</td>
<td>css=#wrap2 iframe</td>
<td></td>
</tr>
<tr>
<td>waitForElementPresent</td>
<td>//canvas</td>
<td></td>
</tr>
<tr>
<td>waitForElementPresent</td>
<td>//div[@class="graph-content" and not(@disabled)]</td>
<td></td>
</tr>
<tr>
<td>verifyImageMatchSnapshot</td>
<td>//canvas</td>
<td>0</td>
</tr>
<!-- TODO: click and check listbox -->
<tr>
<td>selectFrame</td>
<td>relative=top</td>
<td></td>
</tr>
<!-- worklists -->
<tr>
<td>assertText</td>
<td>//h1[@data-i18n="Support Request WorkLists"]</td>
<td>Listes de travail des demandes d'assistance</td>
</tr>
<tr>
<td>assertElementPresent</td>
<td>//div[@data-gadget-scope="worklist"]//ul/li/a[text()="Demandes d'assistance à ouvrir"]/span[text()="1"]</td>
<td></td>
</tr>
</tbody>
</table>
</body>
</html>
\ No newline at end of file
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ZopePageTemplate" module="Products.PageTemplates.ZopePageTemplate"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="_reconstructor" module="copy_reg"/>
</klass>
<tuple>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
<global name="object" module="__builtin__"/>
<none/>
</tuple>
<state>
<dictionary>
<item>
<key> <string>_asgns</string> </key>
<value>
<dictionary>
<item>
<key> <string>name_subpath</string> </key>
<value> <string>traverse_subpath</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>content_type</string> </key>
<value> <string>text/html</string> </value>
</item>
<item>
<key> <string>expand</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>testSubmitSupportRequestTranslation</string> </value>
</item>
<item>
<key> <string>output_encoding</string> </key>
<value> <string>utf-8</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <unicode>Submit Request Request Translation</unicode> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<html>
<head>
<title tal:content="template/title_and_id"></title>
<meta http-equiv="content-type" content="text/html;charset=utf-8">
</head>
<body>
<table cellpadding="1" cellspacing="1" border="1">
<thead>
<tr>
<td rowspan="1" colspan="3" tal:content="template/title_and_id"></td>
</tr>
</thead>
<tbody>
<tal:block metal:use-macro="here/Zuite_SupportRequestUITemplate/macros/init" />
<tal:block metal:use-macro="here/Zuite_SupportRequestUITemplate/macros/cleanup_module" />
<tal:block metal:use-macro="here/Zuite_SupportRequestUITemplate/macros/create_data" />
<tr>
<td>open</td>
<td>${base_url}/web_site_module/erp5_officejs_support_request_ui/fr/</td>
<td></td>
</tr>
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/wait_for_app_loaded" />
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/wait_for_listbox_loaded" />
<tr>
<td>waitForElementPresent</td>
<td>//input[@data-i18n='[value]Submit New Support Request']</td>
<td></td>
</tr>
<tr>
<td>assertValue</td>
<td>//input[@data-i18n='[value]Submit New Support Request']</td>
<td>Soumettre une nouvelle demande d'assistance</td>
</tr>
<tr>
<td>click</td>
<td>//a/input[@data-i18n="[value]Submit New Support Request"]</td>
<td></td>
</tr>
<tr>
<td>waitForElementPresent</td>
<td>//input[@class="dialogconfirm"]</td>
<td></td>
</tr>
<tr>
<td>assertValue</td>
<td>//input[@class="dialogconfirm"]</td>
<td>Soumettre une nouvelle demande d'assistance</td>
</tr>
<tr>
<td>assertText</td>
<td>//h3[contains(@class, "ui-content-title")]</td>
<td>Soumettre une nouvelle demande d'assistance</td>
</tr>
<tr>
<td>setFile</td>
<td>field_your_file</td>
<td>portal_skins/erp5_officejs_support_request_test/test_support_request_text.pdf test_support_request_text.pdf
</td>
</tr>
<tr>
<td>type</td>
<td>field_your_title</td>
<td>à l'aide !</td>
</tr>
<tr>
<td>assertSelected</td>
<td>field_your_project</td>
<td>RobotMaking</td>
</tr>
<tr>
<td>waitForElementPresent</td>
<td>//select[@name="field_your_resource"]/option[text()="FeatureRequire"]</td>
<td></td>
</tr>
<tr>
<td>select</td>
<td>field_your_resource</td>
<td>FeatureRequire</td>
</tr>
<tal:block tal:define="text_content string:Le message">
<tal:block metal:use-macro="container/Zuite_CommonTemplateForRenderjsUi/macros/type_ckeditor_text_content" />
</tal:block>
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/submit_dialog" />
<!-- XXX this is here/Zuite_CommonTemplateForRenderjsUi/macros/wait_for_notification but with a trick to escape the message that already contain a ' which needs to be escaped -->
<tal:block>
<tr>
<td colspan="3"><b>Wait for the notification message</b></td>
</tr>
<tr>
<td>waitForElementPresent</td>
<td>//div[@data-gadget-scope='notification' and @class='visible']//button[@class='success' and
text()="Nouvelle demande d'assistance créée."]</td>
<td></td>
</tr>
<tr>
<td>assertElementPresent</td>
<td>//div[@data-gadget-scope='notification' and @class='visible']//button[@class='success' and
text()="Nouvelle demande d'assistance créée."]</td>
<td></td>
</tr>
<tr>
<td colspan="3">
<p></p>
</td>
</tr>
</tal:block>
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/wait_for_content_loaded" />
<tr>
<td>waitForText</td>
<td>//ol[@id="post_list"]//li[1]//p</td>
<td>Le message</td>
</tr>
<tr>
<td>assertText</td>
<td>//ol[@id="post_list"]//li[1]</td>
<td>regex:Par .*-.*il y a quelques secondes.*</td>
</tr>
<tr>
<td>assertText</td>
<td>//ol[@id="post_list"]//li[1]/strong[2]</td>
<td>Pièce jointe:</td>
</tr>
<tr>
<td>assertText</td>
<td>//p[@data-i18n="Comments:"]</td>
<td>Commentaires:</td>
</tr>
</tbody>
</table>
</body>
</html>
\ No newline at end of file
......@@ -54,7 +54,7 @@
</item>
<item>
<key> <string>title</string> </key>
<value> <unicode></unicode> </value>
<value> <unicode>Support Request Panel Translation</unicode> </value>
</item>
</dictionary>
</pickle>
......
<html>
<head>
<title tal:content="template/title">Support Request Panel Translation</title>
<title tal:content="template/title_and_id"></title>
<meta http-equiv="content-type" content="text/html;charset=utf-8">
</head>
<body>
<table cellpadding="1" cellspacing="1" border="1">
<thead>
<tr><td rowspan="1" colspan="3">Support Request Panel Translation</td></tr>
<tr><td rowspan="1" colspan="3" tal:content="template/title_and_id"></td></tr>
</thead><tbody>
<tal:block metal:use-macro="here/Zuite_SupportRequestUITemplate/macros/init" />
<tal:block metal:use-macro="here/Zuite_SupportRequestUITemplate/macros/cleanup_module" />
......@@ -54,55 +54,55 @@
<tr>
<td>assertText</td>
<td>//div[contains(@class, "panel")]//a[contains(@class, "ui-icon-home")]</td>
<td>//div[@data-gadget-scope="panel"]//a[contains(@class, "ui-icon-home")]</td>
<td>Accueil</td>
</tr>
<tr>
<td>assertText</td>
<td>//div[contains(@class, "panel")]//a[contains(@class, "ui-icon-life-ring")]</td>
<td>//div[@data-gadget-scope="panel"]//a[contains(@class, "ui-icon-life-ring")]</td>
<td>Demandes d'assistance</td>
</tr>
<tr>
<td>assertText</td>
<td>//div[contains(@class, "panel")]//a[contains(@class, "ui-icon-sliders")]</td>
<td>//div[@data-gadget-scope="panel"]//a[contains(@class, "ui-icon-sliders")]</td>
<td>Préférences</td>
</tr>
<tr>
<td>assertText</td>
<td>//div[contains(@class, "panel")]//a[contains(@class, "ui-icon-power-off")]</td>
<td>//div[@data-gadget-scope="panel"]//a[contains(@class, "ui-icon-power-off")]</td>
<td>Déconnexion</td>
</tr>
<tr>
<td>waitForElementPresent</td>
<td>//div[contains(@class, "panel")]//dt[contains(@class, "ui-icon-eye")]</td>
<td>//div[@data-gadget-scope="panel"]//dt[contains(@class, "ui-icon-eye")]</td>
<td></td>
</tr>
<tr>
<td>assertText</td>
<td>//div[contains(@class, "panel")]//dt[contains(@class, "ui-icon-eye")]</td>
<td>//div[@data-gadget-scope="panel"]//dt[contains(@class, "ui-icon-eye")]</td>
<td>Vues</td>
</tr>
<tr>
<td>assertText</td>
<td>//div[contains(@class, "panel")]//dl/dd[1]</td>
<td>//div[@data-gadget-scope="panel"]//dl/dd[1]</td>
<td>Général</td>
</tr>
<tr>
<td>assertText</td>
<td>//div[contains(@class, "panel")]//dl/dd[2]</td>
<td>//div[@data-gadget-scope="panel"]//dl/dd[2]</td>
<td>Historique</td>
</tr>
<tr>
<td>assertText</td>
<td>//div[contains(@class, "panel")]//dt[contains(@class, "ui-icon-cogs")]</td>
<td>//div[@data-gadget-scope="panel"]//dt[contains(@class, "ui-icon-cogs")]</td>
<td>Décisions</td>
</tr>
<tr>
<td>assertElementPresent</td>
<td>//div[contains(@class, "panel")]//dl/dd/a[text() = "Clôturer le ticket"]</td>
<td>//div[@data-gadget-scope="panel"]//dl/dd/a[text() = "Clôturer le ticket"]</td>
<td></td>
</tr>
......
......@@ -19,14 +19,6 @@
<script src="jiodev.js" type="text/javascript"></script>
<script src="domsugar.js" type="text/javascript"></script>
<script id="dialog-button-template" type="text/x-handlebars-template">
{{#if show_update_button}}
<button disabled name="action_update" type="button" data-i18n="Update">Update</button>
{{/if}}
<input disabled name="action_confirm" class="dialogconfirm" type="submit" data-i18n="[value]Proceed" value="Proceed" />
<a class="dialogcancel" data-i18n="Cancel">Cancel</a>
</script>
<!-- custom script -->
<script src="gadget_global.js" type="text/javascript"></script>
<script src="gadget_erp5_global.js" type="text/javascript"></script>
......
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