Commit 4705a2fe authored by Gabriel Monnerat's avatar Gabriel Monnerat

erp5_officejs_support_request_ui: New gadget to generate RSS Link and remove it from dashboard

Having a big "Generate RSS" button on the front page is strange, because the front page button are for common use cases that users are supposed to execute a lot.

For example, makes sense to have a "Submit new Support Request" button, because users are using this app to submit support requests, but generating RSS is very exceptional action, so we wanted to remove it from the front page.

Also, we changed:
- change the generate_rss_link action in Support Request Module, to directly call SupportRequestModule_generateRSSLinkUrl.py instead of the form.
- change SupportRequestModule_generateRSSLinkUrl to display the form at the end.
- Use Base_renderForm like for erp5_crm
- Put the RSS url in the REQUEST
- change the gadget field configuration to use the value from the REQUEST (if not found, put None)
- change the gadget implementation to display the URL in an html5 string field  + a button
parent 97891b1a
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ActionInformation" module="Products.CMFCore.ActionInformation"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>action</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>categories</string> </key>
<value>
<tuple>
<string>action_type/object_jio_action</string>
</tuple>
</value>
</item>
<item>
<key> <string>category</string> </key>
<value> <string>object_jio_action</string> </value>
</item>
<item>
<key> <string>condition</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>icon</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>generate_rss_link</string> </value>
</item>
<item>
<key> <string>permissions</string> </key>
<value>
<tuple>
<string>View</string>
</tuple>
</value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Action Information</string> </value>
</item>
<item>
<key> <string>priority</string> </key>
<value> <float>3.0</float> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Generate RSS</string> </value>
</item>
<item>
<key> <string>visible</string> </key>
<value> <int>1</int> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="Expression" module="Products.CMFCore.Expression"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>text</string> </key>
<value> <string>string:${object_url}/SupportRequestModule_generateRSSLinkUrl</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
......@@ -21,7 +21,6 @@
<div class="ui-field-contain">
<div class="bottom">
<a><input type="submit" class="first-line-buttons ui-disabled" data-i18n="[value]Submit New Support Request" value="Submit New Support Request" disabled /></a>
<a target="_blank"><input class="first-line-buttons ui-disabled" type="submit" data-i18n="[value]Generate RSS" value="Generate RSS" disabled /></a>
</div>
</div>
......
......@@ -238,7 +238,7 @@
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>962.17075.21280.35942</string> </value>
<value> <string>983.3535.62274.35669</string> </value>
</item>
<item>
<key> <string>state</string> </key>
......@@ -256,8 +256,8 @@
</tuple>
<state>
<tuple>
<float>1537518325.21</float>
<string>GMT+9</string>
<float>1592854185.8</float>
<string>UTC</string>
</tuple>
</state>
</object>
......
......@@ -108,7 +108,7 @@
throw error;
})
.push(function () {
var restore_filter_input = gadget.element.querySelectorAll("input")[2];
var restore_filter_input = gadget.element.querySelectorAll("input")[1];
restore_filter_input.disabled = false;
restore_filter_input.classList.remove("ui-disabled");
});
......@@ -264,7 +264,7 @@
})
.declareService(function () {
var gadget = this,
restore_filter_input = gadget.element.querySelectorAll("input")[2];
restore_filter_input = gadget.element.querySelectorAll("input")[1];
return gadget.getUrlParameter('extended_search')
.push(function (result) {
if (result !== undefined) {
......@@ -277,29 +277,8 @@
var gadget = this;
return new RSVP.Queue()
.push(function () {
var generate_rss_input = gadget.element.querySelectorAll("input")[1],
restore_filter_input = gadget.element.querySelectorAll("input")[2],
one = new RSVP.Queue().push(function () {
return promiseEventListener(generate_rss_input, "click", false);
}).push(function () {
generate_rss_input.disabled = true;
generate_rss_input.classList.add("ui-disabled");
return gadget.getSetting("hateoas_url")
.push(function (hateoas_url) {
return gadget.jio_getAttachment(
'support_request_module',
hateoas_url + 'support_request_module'
+ "/SupportRequestModule_generateRSSLinkAsJson"
);
})
.push(function (result) {
generate_rss_input.parentNode.href = result.restricted_access_url;
generate_rss_input.value = "RSS Link";
generate_rss_input.disabled = false;
generate_rss_input.classList.remove("ui-disabled");
});
}),
two = loopEventListener(restore_filter_input, "click", false, function () {
var restore_filter_input = gadget.element.querySelectorAll("input")[1],
one = loopEventListener(restore_filter_input, "click", false, function () {
restore_filter_input.disabled = true;
restore_filter_input.classList.add("ui-disabled");
return gadget.redirect({
......@@ -311,9 +290,7 @@
});
}, true);
generate_rss_input.disabled = false;
generate_rss_input.classList.remove("ui-disabled");
return RSVP.all([one, two]);
return one;
});
})
.onStateChange(function () {
......
......@@ -265,8 +265,6 @@
<state>
<tuple>
<float>1595449693.01</float>
<string>UTC</string>
</tuple>
</state>
</object>
</value>
......
......@@ -238,7 +238,7 @@
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>983.35938.18075.42103</string> </value>
<value> <string>984.51645.41378.19729</string> </value>
</item>
<item>
<key> <string>state</string> </key>
......@@ -256,7 +256,7 @@
</tuple>
<state>
<tuple>
<float>1589291785.06</float>
<float>1593352275.81</float>
<string>UTC</string>
</tuple>
</state>
......
......@@ -27,8 +27,10 @@
//////////////////////////////////////////////
// acquired method
//////////////////////////////////////////////
.declareAcquiredMethod("jio_getAttachment", "jio_getAttachment")
.declareAcquiredMethod("getUrlFor", "getUrlFor")
.declareAcquiredMethod("translateHtml", "translateHtml")
.declareAcquiredMethod("getSetting", "getSetting")
.declareAcquiredMethod("redirect", "redirect")
/////////////////////////////////////////////////////////////////
......@@ -260,7 +262,12 @@
});
}, 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
......
......@@ -238,7 +238,7 @@
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>976.15074.42522.46830</string> </value>
<value> <string>984.51645.41378.19729</string> </value>
</item>
<item>
<key> <string>state</string> </key>
......@@ -256,7 +256,7 @@
</tuple>
<state>
<tuple>
<float>1559893029.52</float>
<float>1593352467.41</float>
<string>UTC</string>
</tuple>
</state>
......
import json
absolute_url = context.absolute_url()
request_url = "%s/SupportRequestModule_viewLastSupportRequestListAsRss" % (absolute_url,)
portal = context.getPortalObject()
person = portal.portal_membership.getAuthenticatedMember().getUserValue()
if person is None:
return json.dumps({'restricted_access_url': request_url})
access_token = None
for token_item in portal.portal_catalog(
portal_type="Restricted Access Token",
default_agent_uid=person.getUid(),
validation_state='validated'
):
if token_item.getUrlString() == request_url:
access_token = token_item
reference = access_token.getReference()
break
if access_token is None:
access_token = portal.access_token_module.newContent(
portal_type="Restricted Access Token",
url_string=request_url,
url_method="GET",
)
access_token.setAgentValue(person)
reference = access_token.getReference()
access_token.validate()
url = "%s?access_token=%s&access_token_secret=%s" % (
request_url,
access_token.getId(),
reference)
return json.dumps({'restricted_access_url': url})
absolute_url = context.absolute_url()
request_url = "%s/SupportRequestModule_viewLastSupportRequestListAsRss" % (absolute_url,)
portal = context.getPortalObject()
person = portal.portal_membership.getAuthenticatedMember().getUserValue()
if person is None:
url = request_url
else:
access_token = None
for token_item in portal.portal_catalog(
portal_type="Restricted Access Token",
default_agent_uid=person.getUid(),
validation_state='validated'
):
if token_item.getUrlString() == request_url:
access_token = token_item
reference = access_token.getReference()
break
if access_token is None:
access_token = portal.access_token_module.newContent(
portal_type="Restricted Access Token",
url_string=request_url,
url_method="GET",
)
access_token.setAgentValue(person)
reference = access_token.getReference()
access_token.validate()
url = "%s?access_token=%s&access_token_secret=%s" % (
request_url,
access_token.getId(),
reference)
context.REQUEST.form["your_rss_url"] = url
return context.Base_renderForm('SupportRequestModule_viewGenerateRSSLinkDialog', message='URL generated')
......@@ -50,11 +50,11 @@
</item>
<item>
<key> <string>_params</string> </key>
<value> <string></string> </value>
<value> <string>**kw</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>SupportRequestModule_generateRSSLinkAsJson</string> </value>
<value> <string>SupportRequestModule_generateRSSLinkUrl</string> </value>
</item>
</dictionary>
</pickle>
......
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ERP5 Form" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_objects</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>action</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>action_title</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>edit_order</string> </key>
<value>
<list/>
</value>
</item>
<item>
<key> <string>encoding</string> </key>
<value> <string>UTF-8</string> </value>
</item>
<item>
<key> <string>enctype</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>group_list</string> </key>
<value>
<list>
<string>left</string>
<string>right</string>
<string>center</string>
<string>bottom</string>
<string>hidden</string>
</list>
</value>
</item>
<item>
<key> <string>groups</string> </key>
<value>
<dictionary>
<item>
<key> <string>bottom</string> </key>
<value>
<list/>
</value>
</item>
<item>
<key> <string>center</string> </key>
<value>
<list/>
</value>
</item>
<item>
<key> <string>hidden</string> </key>
<value>
<list/>
</value>
</item>
<item>
<key> <string>left</string> </key>
<value>
<list>
<string>generate_rss_link_gadget</string>
<string>your_rss_url</string>
</list>
</value>
</item>
<item>
<key> <string>right</string> </key>
<value>
<list/>
</value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>SupportRequestModule_viewGenerateRSSLinkDialog</string> </value>
</item>
<item>
<key> <string>method</string> </key>
<value> <string>POST</string> </value>
</item>
<item>
<key> <string>name</string> </key>
<value> <string>SupportRequestModule_viewGenerateRSSLinkDialog</string> </value>
</item>
<item>
<key> <string>pt</string> </key>
<value> <string>form_dialog</string> </value>
</item>
<item>
<key> <string>row_length</string> </key>
<value> <int>4</int> </value>
</item>
<item>
<key> <string>stored_encoding</string> </key>
<value> <string>UTF-8</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Click below to copy to clipboard</string> </value>
</item>
<item>
<key> <string>unicode_mode</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>update_action</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>update_action_title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ProxyField" module="Products.ERP5Form.ProxyField"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>delegated_list</string> </key>
<value>
<list>
<string>enabled</string>
<string>title</string>
</list>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>your_rss_url</string> </value>
</item>
<item>
<key> <string>message_values</string> </key>
<value>
<dictionary>
<item>
<key> <string>external_validator_failed</string> </key>
<value> <string>The input failed the external validator.</string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>overrides</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>tales</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>values</string> </key>
<value>
<dictionary>
<item>
<key> <string>enabled</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>field_id</string> </key>
<value> <string>my_string_field</string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string>Base_viewFieldLibrary</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>RSS Link</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
div[data-gadget-url$="gadget_generate_rss_link.html"] button {
color: #212529;
padding: 3pt;
border: 1px solid rgba(0, 0, 0, 0.14);
border-radius: 0.325em;
display: inline-block;
margin-top: 6pt;
}
div[data-gadget-url$="gadget_generate_rss_link.html"] button:before {
padding-right: 4pt;
}
\ No newline at end of file
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="File" module="OFS.Image"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>__name__</string> </key>
<value> <string>gadget_generate_rss_link.css</string> </value>
</item>
<item>
<key> <string>content_type</string> </key>
<value> <string>text/css</string> </value>
</item>
<item>
<key> <string>precondition</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?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="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
</klass>
<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>gadget_generate_rss_link.html</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></unicode> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<!DOCTYPE html>
<html>
<head>
<!--
data-i18n=Copied
-->
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, user-scalable=no" />
<link rel="stylesheet" href="gadget_generate_rss_link.css">
<script src="rsvp.js" type="text/javascript"></script>
<script src="renderjs.js" type="text/javascript"></script>
<script src="domsugar.js" type="text/javascript"></script>
<script src="gadget_generate_rss_link.js" type="text/javascript"></script>
</head>
<body>
<div data-gadget-url="gadget_html5_input.html" data-gadget-scope="text_field"></div>
<button type="button" class="ui-btn-icon-left ui-icon-copy">Copy to Clipboard</button>
</body>
</html>
\ No newline at end of file
/*global rJS, window, navigator*/
/*jslint nomen: true, maxlen:80, indent:2*/
(function (rJS, window, navigator) {
'use strict';
rJS(window)
.declareAcquiredMethod("getTranslationList", "getTranslationList")
.declareAcquiredMethod("notifyChange", "notifyChange")
.declareMethod('render', function (options) {
return this.changeState({
rss_url: options.rss_url
});
})
.onStateChange(function () {
var gadget = this;
return gadget.getDeclaredGadget('text_field')
.push(function (text_gadget) {
return text_gadget.render({
value: gadget.state.rss_url
});
});
})
.onEvent('click', function (evt) {
var tag_name = evt.target.tagName,
gadget = this,
button_text;
if (tag_name !== 'BUTTON') {
return;
}
// Disable any button. It must be managed by this gadget
evt.preventDefault();
return gadget.getTranslationList(["Copied"])
.push(function (result) {
button_text = result[0];
return navigator.clipboard.writeText(gadget.state.rss_url);
})
.push(function () {
return gadget.notifyChange({
"message": button_text,
"status": "success"
});
});
}, false, false);
}(rJS, window, navigator));
\ No newline at end of file
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="File" module="OFS.Image"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>gadget_generate_rss_link.js</string> </value>
</item>
<item>
<key> <string>content_type</string> </key>
<value> <string>application/javascript</string> </value>
</item>
<item>
<key> <string>precondition</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
Support Request Module | fast_input
Support Request Module | generate_rss_link
Support Request Module | officejs_support_request_view
Support Request Module | view_last_support_request
Support Request | officejs_support_request_view
\ No newline at end of file
......@@ -461,15 +461,14 @@ class TestSupportRequestRSSSOneEvent(SupportRequestRSSTestCase, DefaultTestRSSMi
self._checkRSS(response)
def test_RSS_with_token(self):
response = self.publish(
"%s/support_request_module/SupportRequestModule_generateRSSLinkAsJson" % self.getWebSite().getPath(),
basic='%s:%s' % (self.user.erp5_login.getReference(), self.user_password))
restricted_access_url = json.loads(response.getBody())['restricted_access_url']
# make it relative url
self.login(self.user.getUserId())
# get rss link url
self.portal.support_request_module.SupportRequestModule_generateRSSLinkUrl()
restricted_access_url = self.portal.REQUEST.form["your_rss_url"]
parsed_url = urlparse.urlparse(restricted_access_url)
restricted_access_url = restricted_access_url.replace(
'%s://%s' % (parsed_url.scheme, parsed_url.netloc), '', 1)
# and check it (this time the request is not basic-authenticated)
# and check it
self._checkRSS(self.publish(restricted_access_url))
......
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