Commit 8a5e226b authored by Rafael Monnerat's avatar Rafael Monnerat

slapos_rss_style: convert to title, description, author to compatible xml

  Ensure to convert the text values to a safer output that will be compatible with XML 1.0.
  See: https://www.w3.org/TR/REC-xml/#NT-Char
parent 787a9271
Pipeline #37725 passed with stage
in 0 seconds
from zExceptions import Unauthorized
from Products.Formulator.Widget import convert_to_xml_compatible_string
def convertToSafeXML(self, value, REQUEST=None):
if REQUEST is not None:
raise Unauthorized
if not value:
return value
return convert_to_xml_compatible_string(value)
\ No newline at end of file
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Extension Component" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_recorded_property_dict</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>default_reference</string> </key>
<value> <string>SlapOSRSS</string> </value>
</item>
<item>
<key> <string>default_source_reference</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>extension.erp5.SlapOSRSS</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Extension Component</string> </value>
</item>
<item>
<key> <string>sid</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>text_content_error_message</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>text_content_warning_message</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>version</string> </key>
<value> <string>erp5</string> </value>
</item>
<item>
<key> <string>workflow_history</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<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="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary>
<item>
<key> <string>component_validation_workflow</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAQ=</string> </persistent>
</value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="4" aka="AAAAAAAAAAQ=">
<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>validate</string> </value>
</item>
<item>
<key> <string>validation_state</string> </key>
<value> <string>validated</string> </value>
</item>
</dictionary>
</list>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ExternalMethod" module="Products.ExternalMethod.ExternalMethod"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_function</string> </key>
<value> <string>convertToSafeXML</string> </value>
</item>
<item>
<key> <string>_module</string> </key>
<value> <string>SlapOSRSS</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>Base_convertToSafeXML</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
......@@ -56,11 +56,13 @@ for brain in portal.portal_catalog(
data_list.append(
Object(**{
'title': "[%s] %s" % (ticket_category.upper(), ticket_title),
'title': context.Base_convertToSafeXML(
"[%s] %s" % (ticket_category.upper(), ticket_title)),
'category': ticket_category,
'author': event.getSourceTitle(checked_permission="View"),
'author': context.Base_convertToSafeXML(
event.getSourceTitle(checked_permission="View")),
'link': ticket_link,
'description': event.getTextContent(),
'description': context.Base_convertToSafeXML(event.getTextContent()),
'pubDate': event.getStartDate(),
'guid': '{}-{}'.format(
event.getFollowUp(),
......
......@@ -24,6 +24,7 @@
from erp5.component.test.SlapOSTestCaseMixin import SlapOSTestCaseMixinWithAbort,\
TemporaryAlarmScript, PinnedDateTime
from Products.ERP5Type.Utils import unicode2str
from DateTime import DateTime
import feedparser
from time import sleep
......@@ -133,9 +134,53 @@ class TestRSSSyleSkinsMixin(SlapOSTestCaseMixinWithAbort):
self.tic()
return support_request
class TestSlapOSSupportRequestRSS(TestRSSSyleSkinsMixin):
def test_WebSection_viewTicketListAsRSS_invalid_char(self):
person = self.makePerson(self.addProject())
support_request = person.Entity_createTicketFromTradeCondition(
'service_module/slapos_crm_monitoring',
'Broken \08x',
'I need help !\08x',
)
support_request.Ticket_createProjectEvent(
support_request.getTitle(), 'incoming', 'Web Message',
support_request.getResource(),
text_content=support_request.getDescription(),
content_type='text/plain',
source=person.getRelativeUrl()
)
self.tic()
self.login(person.getUserId())
self.portal.portal_skins.changeSkin('RSS')
parsed = feedparser.parse(self.portal.WebSection_viewTicketListAsRSS())
self.assertFalse(parsed.bozo)
first_entry_id = [item.id for item in parsed.entries]
self.assertEqual([item.summary for item in parsed.entries],
[u'I need help !\ufffd8x'])
self.logout()
sleep(2)
self.login()
support_request.Ticket_createProjectEvent(
support_request.getTitle(), 'outgoing', 'Web Message',
support_request.getResource(),
text_content='How can I \08xhelp you ?',
content_type='text/plain'
)
self.tic()
self.logout()
self.login(person.getUserId())
self.portal.portal_skins.changeSkin('RSS')
parsed = feedparser.parse(self.portal.WebSection_viewTicketListAsRSS())
self.assertFalse(parsed.bozo)
self.assertEqual([item.summary for item in parsed.entries],
[u'How can I \ufffd8xhelp you ?', u'I need help !\ufffd8x'])
self.assertNotEqual([item.id for item in parsed.entries][0], first_entry_id)
def test_WebSection_viewTicketListAsRSS(self):
person = self.makePerson(self.addProject())
......@@ -195,7 +240,7 @@ class TestSlapOSWebSection_getEventList(TestRSSSyleSkinsMixin):
def test_WebSection_getEventList(self, web_site=None):
# WebSection_getEventList is already widely tested on Base_getTicketRelatedEventList
# and Folder_getOpenTicketList, so we only tested the specific use case of
# and Folder_getOpenTicketList, so we only tested the specific use case of
# all events togheter
if web_site is None:
web_site = self.portal
......@@ -251,11 +296,12 @@ class TestSlapOSWebSection_getEventList(TestRSSSyleSkinsMixin):
# Extra checks
self.assertNotEqual(open_ticket_list[0].pubDate, None)
self.assertNotEqual(open_ticket_list[0].link, None)
self.assertIn(event.getTextContent(), open_ticket_list[0].description)
self.assertIn(event.getTextContent(),
unicode2str(open_ticket_list[0].description))
self.assertEqual(open_ticket_list[0].guid,
'{}-{}'.format(event.getFollowUp(),
event.getRelativeUrl()))
self.assertEqual(open_ticket_list[0].title,
self.assertEqual(unicode2str(open_ticket_list[0].title),
'[MONITORING] %s' % ticket.getTitle())
self.assertIn("%s/#/" % web_site.absolute_url(),
open_ticket_list[0].link)
......@@ -317,11 +363,12 @@ class TestSlapOSWebSection_getEventList(TestRSSSyleSkinsMixin):
# Extra checks
self.assertNotEqual(open_ticket_list[0].pubDate, None)
self.assertNotEqual(open_ticket_list[0].link, None)
self.assertIn(event_rr.getTextContent(), open_ticket_list[0].description)
self.assertIn(event_rr.getTextContent(),
unicode2str(open_ticket_list[0].description))
self.assertEqual(open_ticket_list[0].guid,
'{}-{}'.format(event_rr.getFollowUp(),
event_rr.getRelativeUrl()))
self.assertEqual(open_ticket_list[0].title,
self.assertEqual(unicode2str(open_ticket_list[0].title),
'[ACKNOWLEDGEMENT] %s' % regularisation_request.getTitle())
self.assertIn("%s/#/" % web_site.absolute_url(),
open_ticket_list[0].link)
......@@ -408,19 +455,19 @@ class TestSlapOSWebSection_getEventList(TestRSSSyleSkinsMixin):
event_ud.getRelativeUrl()))
# check if ordering is correct.
self.assertEqual(open_ticket_list[0].title,
self.assertEqual(unicode2str(open_ticket_list[0].title),
'[THEIA IDE] %s' % upgrade_decision.getTitle())
self.assertIn("%s/#/" % web_site.absolute_url(),
open_ticket_list[1].link)
self.assertEqual(open_ticket_list[1].title,
self.assertEqual(unicode2str(open_ticket_list[1].title),
'[ACKNOWLEDGEMENT] %s' % regularisation_request.getTitle())
self.assertIn("%s/#/" % web_site.absolute_url(),
open_ticket_list[1].link)
self.assertEqual(open_ticket_list[2].title,
self.assertEqual(unicode2str(open_ticket_list[2].title),
'[MONITORING] %s' % ticket.getTitle())
self.assertIn("%s/#/" % web_site.absolute_url(),
......@@ -481,7 +528,7 @@ class TestWebSection_getLegacyMessageList(TestRSSSyleSkinsMixin):
self.assertEqual('This RSS is disabled (20241001)', message.title)
self.assertIn('This RSS feed is disabled:', message.description)
self.assertIn('?date=20241001', message.link)
def testWebSection_getLegacyMessageList_instance_tree(self):
# Test instance tree legacy since we patch InstanceTree_view
web_site = self.portal.web_site_module.slapos_master_panel.Base_createCloneDocument(batch_mode=1)
......@@ -503,10 +550,4 @@ class TestWebSection_getLegacyMessageList(TestRSSSyleSkinsMixin):
self.assertEqual(message.author, 'Administrator')
self.assertEqual('This RSS is disabled (20241001)', message.title)
self.assertIn('This RSS feed is disabled:', message.description)
self.assertIn('?date=20241001', message.link)
\ No newline at end of file
self.assertIn('?date=20241001', message.link)
\ No newline at end of file
extension.erp5.SlapOSRSS
\ No newline at end of file
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