Commit 2febcb83 authored by Gabriel Monnerat's avatar Gabriel Monnerat Committed by Georgios Dagkakis

WebSection: Fix RJS website rendering when trailing / in the URL is missing

renderJS gadget HTML uses relative URLs to link to there javascript/CSS files.
Absolute URL calculation is resolved dynamically by the browser when loading the gadget.

When accessing an ERP5 web section without a trailing / in the URL, the browser will calculate absolute URL from the parent document and not the web site itself.

Example:
In http://foo.com/web_site_module/bar , the relative URL couscous.js will be resolved http://foo.com/web_site_module/couscous.js
If couscous.js is a document from DMS (Web Page for example), such URL can not be resolved and leads to a 404 error.

One solution to solve this is to redirect (302) the browser when accessing a Web Section (Web Site is a web section) directly in ERP5.
Example:
http://foo.com/web_site_module/bar ->  http://foo.com/web_site_module/bar/
But  http://foo.com/web_site_module/bar/view should not redirect
parent 0be3f1c9
...@@ -226,6 +226,14 @@ class WebSection(Domain, DocumentExtensibleTraversableMixin): ...@@ -226,6 +226,14 @@ class WebSection(Domain, DocumentExtensibleTraversableMixin):
if self.REQUEST.get(self.web_section_key, MARKER) is MARKER: if self.REQUEST.get(self.web_section_key, MARKER) is MARKER:
self.REQUEST[self.web_section_key] = self.getPhysicalPath() self.REQUEST[self.web_section_key] = self.getPhysicalPath()
self.REQUEST.set('current_web_section', self) self.REQUEST.set('current_web_section', self)
actual_url = self.REQUEST.get("ACTUAL_URL", "").strip()
if actual_url and actual_url in actual_url and not actual_url.endswith("/"):
query_string = self.REQUEST.get("QUERY_STRING", "")
query_str = "?%s" % query_string if query_string else query_string
return self.REQUEST.RESPONSE.redirect(
"".join([actual_url, "/", query_str]))
if not self.REQUEST.get('editable_mode') and not self.REQUEST.get('ignore_layout'): if not self.REQUEST.get('editable_mode') and not self.REQUEST.get('ignore_layout'):
document = None document = None
if self.isDefaultPageDisplayed(): if self.isDefaultPageDisplayed():
...@@ -403,6 +411,23 @@ class WebSection(Domain, DocumentExtensibleTraversableMixin): ...@@ -403,6 +411,23 @@ class WebSection(Domain, DocumentExtensibleTraversableMixin):
return result return result
def _add_trailing_slash(self, path):
path += "" if path.endswith("/") else "/"
return path
def absolute_url_path(self):
absolute_url_path = self.absolute_url(relative=1)
if not absolute_url_path.startswith("/"):
absolute_url_path = "/" + absolute_url_path
return absolute_url_path
def absolute_url(self, relative=0):
"""
Return absolute_url with / in the end to avoid redirections when
accessing Web sections. Please check the method WebSection.__call__
"""
return self._add_trailing_slash(Domain.absolute_url(self, relative=relative))
security.declareProtected(Permissions.View, 'getSiteMapTree') security.declareProtected(Permissions.View, 'getSiteMapTree')
def getSiteMapTree(self, **kw): def getSiteMapTree(self, **kw):
""" """
......
...@@ -236,6 +236,60 @@ class TestERP5Web(ERP5TypeTestCase): ...@@ -236,6 +236,60 @@ class TestERP5Web(ERP5TypeTestCase):
return webpage_list return webpage_list
def test_WebSection_add_trailing_slash_in_url(self):
"""
When accessing an ERP5 web section without a trailing / in the URL, the
browser will calculate absolute URL from the parent document and not the web
site itself.
Example:
In http://foo.com/web_site_module/bar , the relative URL couscous.js will
be resolved http://foo.com/web_site_module/couscous.js If couscous.js is a
document from DMS (Web Page for example), such URL can not be resolved and
leads to a 404 error.
One solution to solve this is to redirect (302) the browser when accessing a
Web Section (Web Site is a web section) directly in ERP5.
Example:
http://foo.com/web_site_module/bar -> http://foo.com/web_site_module/bar/
But http://foo.com/web_site_module/bar/view should not redirect
"""
# Web Site as context
website = self.setupWebSite()
response = self.publish(website.absolute_url_path()[:-1])
self.assertEqual(MOVED_TEMPORARILY, response.status)
response = self.publish(
"%s?ignore_layout:int=1" % website.absolute_url_path()[:-1])
self.assertEqual("%s?ignore_layout:int=1" % website.absolute_url(),
response.headers.get("location"))
self.assertEqual(MOVED_TEMPORARILY, response.status)
response = self.publish(
"%s/getTitle?ignore_layout:int=1" % website.absolute_url_path())
self.assertEqual(HTTP_OK, response.status)
self.assertEqual("test", response.body)
response = self.publish(
"%s/getTitle" % website.absolute_url_path())
self.assertEqual(HTTP_OK, response.status)
self.assertEqual("test", response.body)
response = self.publish(
"%s/a_non_existing_page" % website.absolute_url_path())
self.assertEqual(404, response.status)
# Web Section as context
websection = self.setupWebSection()
response = self.publish(
"%s?ignore_layout:int=1" % websection.absolute_url_path()[:-1])
self.assertEqual("%s?ignore_layout:int=1" % websection.absolute_url(),
response.headers.get("location"))
self.assertEqual(MOVED_TEMPORARILY, response.status)
response = self.publish(
"%s/getTitle?ignore_layout:int=1" % websection.absolute_url_path())
self.assertEqual(HTTP_OK, response.status)
self.assertEqual("1", response.body)
response = self.publish(
"%s/getTitle" % websection.absolute_url_path())
self.assertEqual(HTTP_OK, response.status)
self.assertEqual("1", response.body)
def test_01_WebSiteRecatalog(self): def test_01_WebSiteRecatalog(self):
""" """
Test that a recataloging works for Web Site documents Test that a recataloging works for Web Site documents
...@@ -440,11 +494,12 @@ Hé Hé Hé!""", page.asText().strip()) ...@@ -440,11 +494,12 @@ Hé Hé Hé!""", page.asText().strip())
self.assertEqual(web_page_en, websection.getDefaultDocumentValue()) self.assertEqual(web_page_en, websection.getDefaultDocumentValue())
# and make sure that the base meta tag which is generated # and make sure that the base meta tag which is generated
# uses the web section rather than the portal # uses the web section rather than the portal
self.REQUEST.set("ACTUAL_URL", websection.absolute_url())
html_page = websection() html_page = websection()
from Products.ERP5.Document.Document import Document from Products.ERP5.Document.Document import Document
base_list = re.findall(Document.base_parser, str(html_page)) base_list = re.findall(Document.base_parser, str(html_page))
base_url = base_list[0] base_url = base_list[0]
self.assertEqual(base_url, "%s/%s/" % (websection.absolute_url(), self.assertEqual(base_url, "%s%s/" % (websection.absolute_url(),
web_page_en.getReference())) web_page_en.getReference()))
def test_06b_DefaultDocumentForWebSite(self): def test_06b_DefaultDocumentForWebSite(self):
...@@ -477,11 +532,12 @@ Hé Hé Hé!""", page.asText().strip()) ...@@ -477,11 +532,12 @@ Hé Hé Hé!""", page.asText().strip())
self.assertEqual(web_page_en, website.getDefaultDocumentValue()) self.assertEqual(web_page_en, website.getDefaultDocumentValue())
# and make sure that the base meta tag which is generated # and make sure that the base meta tag which is generated
# uses the web site rather than the portal # uses the web site rather than the portal
self.REQUEST.set("ACTUAL_URL", website.absolute_url())
html_page = website() html_page = website()
from Products.ERP5.Document.Document import Document from Products.ERP5.Document.Document import Document
base_list = re.findall(Document.base_parser, str(html_page)) base_list = re.findall(Document.base_parser, str(html_page))
base_url = base_list[0] base_url = base_list[0]
self.assertEqual(base_url, "%s/%s/" % (website.absolute_url(), web_page_en.getReference())) self.assertEqual(base_url, "%s%s/" % (website.absolute_url(), web_page_en.getReference()))
def test_07_getDocumentValueList(self): def test_07_getDocumentValueList(self):
""" Check getting getDocumentValueList from Web Section. """ Check getting getDocumentValueList from Web Section.
...@@ -868,7 +924,7 @@ Hé Hé Hé!""", page.asText().strip()) ...@@ -868,7 +924,7 @@ Hé Hé Hé!""", page.asText().strip())
website_relative_url = website.absolute_url(relative=1) website_relative_url = website.absolute_url(relative=1)
website_fr = self.portal.restrictedTraverse( website_fr = self.portal.restrictedTraverse(
'web_site_module/%s/fr' % website_id) 'web_site_module/%s/fr' % website_id)
website_relative_url_fr = '%s/fr' % website_relative_url website_relative_url_fr = '%sfr/' % website_relative_url
websection_id = self.setupWebSection().getId() websection_id = self.setupWebSection().getId()
websection = self.portal.restrictedTraverse( websection = self.portal.restrictedTraverse(
...@@ -876,7 +932,7 @@ Hé Hé Hé!""", page.asText().strip()) ...@@ -876,7 +932,7 @@ Hé Hé Hé!""", page.asText().strip())
websection_relative_url = websection.absolute_url(relative=1) websection_relative_url = websection.absolute_url(relative=1)
websection_fr = self.portal.restrictedTraverse( websection_fr = self.portal.restrictedTraverse(
'web_site_module/%s/fr/%s' % (website_id, websection_id)) 'web_site_module/%s/fr/%s' % (website_id, websection_id))
websection_relative_url_fr = '%s/%s' % (website_relative_url_fr, websection_relative_url_fr = '%s%s/' % (website_relative_url_fr,
websection.getId()) websection.getId())
page_ref = 'foo' page_ref = 'foo'
......
...@@ -395,6 +395,7 @@ class TestERP5WebWithDms(ERP5TypeTestCase, ZopeTestCase.Functional): ...@@ -395,6 +395,7 @@ class TestERP5WebWithDms(ERP5TypeTestCase, ZopeTestCase.Functional):
# check Unauthorized exception is raised for anonymous when authorization_forced is set # check Unauthorized exception is raised for anonymous when authorization_forced is set
self.logout() self.logout()
self.REQUEST.set("ACTUAL_URL", websection.absolute_url())
self.assertEqual(None, websection.getDefaultDocumentValue()) self.assertEqual(None, websection.getDefaultDocumentValue())
self.assertRaises(Unauthorized, websection) self.assertRaises(Unauthorized, websection)
......
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