Skip to content

Commit

Permalink
Merge branch 'test_improvments' of git://github.com/fivethreeo/django…
Browse files Browse the repository at this point in the history
…-cms into test_improvements
  • Loading branch information
Jonas Obrist committed Dec 30, 2010
2 parents 6a11b53 + eb31cc7 commit c15be12
Show file tree
Hide file tree
Showing 22 changed files with 248 additions and 205 deletions.
102 changes: 55 additions & 47 deletions cms/middleware/multilingual.py
@@ -1,11 +1,12 @@
import re
import urllib
from django.middleware.locale import LocaleMiddleware
from django.utils.cache import patch_vary_headers
from django.utils import translation
from django.conf import settings
from cms.utils.i18n import get_default_language
from django.conf import settings
from django.core.urlresolvers import reverse
from django.middleware.locale import LocaleMiddleware
from django.utils import translation
from django.utils.cache import patch_vary_headers
import re
import time
import urllib

SUPPORTED = dict(settings.CMS_LANGUAGES)

Expand All @@ -18,11 +19,52 @@ def has_lang_prefix(path):
else:
return False

def patch_response(content, pages_root, language):
# Customarily user pages are served from http://the.server.com/~username/
# When a user uses django-cms for his pages, the '~' of the url appears quoted in href links.
# We have to quote pages_root for the regular expression to match.
#
# The used regex is quite complex. The exact pattern depends on the used settings.
# The regex extracts the path of the url without the leading page root, but only matches urls
# that don't already contain a language string or aren't considered multilingual.
#
# Here is an annotated example pattern (_r_ is a shorthand for the value of pages_root):
# pattern: <a([^>]+)href=("|\')(?=_r_)(?!(/fr/|/de/|/en/|/pt-br/|/media/|/media/admin/))(_r_(.*?))("|\')(.*?)>
# |-\1--| |-\2-| |---------------------\3---------------------| | |-\5--|||-\6-||-\7-|
# |---\4---|
# input (_r_=/): <a href="/admin/password_change/" class="foo">
# matched groups: (u' ', None, u'/admin/password_change/', u'admin/password_change/', u' class="foo"')
#
# Notice that (?=...) and (?!=...) do not consume input or produce a group in the match object.
# If the regex matches, the extracted path we want is stored in the fourth group (\4).
quoted_root = urllib.quote(pages_root)
HREF_URL_FIX_RE = re.compile(ur'<a([^>]+)href=("|\')(?=%s)(?!(%s|%s|%s))(%s(.*?))("|\')(.*?)>' % (
quoted_root,
"|".join(map(lambda l: quoted_root + l[0] + "/" , settings.CMS_LANGUAGES)),
settings.MEDIA_URL,
settings.ADMIN_MEDIA_PREFIX,
quoted_root
))

# Unlike in href links, the '~' (see above) the '~' in form actions appears unquoted.
#
# For understanding this regex, please read the documentation for HREF_URL_FIX_RE above.
FORM_URL_FIX_RE = re.compile(ur'<form([^>]+)action=("|\')(?=%s)(?!(%s|%s|%s))(%s(.*?))("|\')(.*?)>' % (
pages_root,
"|".join(map(lambda l: pages_root + l[0] + "/" , settings.CMS_LANGUAGES)),
settings.MEDIA_URL,
settings.ADMIN_MEDIA_PREFIX,
pages_root
))

content = HREF_URL_FIX_RE.sub(ur'<a\1href=\2/%s%s\5\6\7>' % (language, pages_root), content)
content = FORM_URL_FIX_RE.sub(ur'<form\1action=\2%s%s/\5\6\7>' % (pages_root, language), content).encode("utf8")
return content

class MultilingualURLMiddleware:
def get_language_from_request (self,request):
changed = False
prefix = has_lang_prefix(request.path_info)
pages_root = urllib.unquote(reverse("pages-root"))
if prefix:
request.path = "/" + "/".join(request.path.split("/")[2:])
request.path_info = "/" + "/".join(request.path_info.split("/")[2:])
Expand Down Expand Up @@ -61,56 +103,22 @@ def process_response(self, request, response):

# note: pages_root is assumed to end in '/'.
# testing this and throwing an exception otherwise, would probably be a good idea
pages_root = urllib.unquote(reverse("pages-root"))


if not path.startswith(settings.MEDIA_URL) and \
not path.startswith(settings.ADMIN_MEDIA_PREFIX) and \
response.status_code == 200 and \
response._headers['content-type'][1].split(';')[0] == "text/html":
pages_root = urllib.unquote(reverse("pages-root"))
try:
decoded_response = response.content.decode('utf-8')
except UnicodeDecodeError:
decoded_response = response.content

# Customarily user pages are served from http://the.server.com/~username/
# When a user uses django-cms for his pages, the '~' of the url appears quoted in href links.
# We have to quote pages_root for the regular expression to match.
#
# The used regex is quite complex. The exact pattern depends on the used settings.
# The regex extracts the path of the url without the leading page root, but only matches urls
# that don't already contain a language string or aren't considered multilingual.
#
# Here is an annotated example pattern (_r_ is a shorthand for the value of pages_root):
# pattern: <a([^>]+)href="(?=_r_)(?!(/fr/|/de/|/en/|/pt-br/|/media/|/media/admin/))(_r_([^"]*))"([^>]*)>
# |-\1--| |---------------------\2---------------------| | |-\4--|| |-\5--|
# |----\3----|
# input (_r_=/): <a href="/admin/password_change/" class="foo">
# matched groups: (u' ', None, u'/admin/password_change/', u'admin/password_change/', u' class="foo"')
#
# Notice that (?=...) and (?!=...) do not consume input or produce a group in the match object.
# If the regex matches, the extracted path we want is stored in the fourth group (\4).
HREF_URL_FIX_RE = re.compile(ur'<a([^>]+)href="(?=%s)(?!(%s|%s|%s))(%s([^"]*))"([^>]*)>' % (
urllib.quote(pages_root),
"|".join(map(lambda l: urllib.quote(pages_root) + l[0] + "/" , settings.CMS_LANGUAGES)),
settings.MEDIA_URL,
settings.ADMIN_MEDIA_PREFIX,
urllib.quote(pages_root)
))

# Unlike in href links, the '~' (see above) the '~' in form actions appears unquoted.
#
# For understanding this regex, please read the documentation for HREF_URL_FIX_RE above.
FORM_URL_FIX_RE = re.compile(ur'<form([^>]+)action="(?=%s)(?!(%s|%s|%s))(%s([^"]*))"([^>]*)>' % (
response.content = patch_response(
decoded_response,
pages_root,
"|".join(map(lambda l: pages_root + l[0] + "/" , settings.CMS_LANGUAGES)),
settings.MEDIA_URL,
settings.ADMIN_MEDIA_PREFIX,
pages_root
))

# Documentation comments for HREF_URL_FIX_RE above explain each match group (\1, \4, \5) represents.
decoded_response = HREF_URL_FIX_RE.sub(ur'<a\1href="/%s%s\4"\5>' % (request.LANGUAGE_CODE, pages_root), decoded_response)
response.content = FORM_URL_FIX_RE.sub(ur'<form\1action="%s%s/\4"\5>' % (pages_root, request.LANGUAGE_CODE), decoded_response).encode("utf8")
request.LANGUAGE_CODE
)

if (response.status_code == 301 or response.status_code == 302 ):
location = response['Location']
Expand Down
9 changes: 9 additions & 0 deletions cms/models/managers.py
Expand Up @@ -146,6 +146,8 @@ def get_title(self, page, language, language_fallback=False):
return None
except self.model.DoesNotExist:
pass
else:
raise
return None

def get_page_slug(self, slug, site=None):
Expand Down Expand Up @@ -455,17 +457,24 @@ def __get_id_list(self, user, site, attr):
qs.order_by('page__tree_id', 'page__level', 'page__lft')
# default is denny...
page_id_allow_list = []
desc_list = []
for permission in qs:
desc_list.append((permission.page.get_descendants(include_self=True), permission.page.pk, 'grant %i' % permission.grant_on))

is_allowed = getattr(permission, attr)
if is_allowed:

# can add is special - we are actually adding page under current page
if permission.grant_on & MASK_PAGE or attr is "can_add":
page_id_allow_list.append(permission.page.id)
if permission.grant_on & MASK_CHILDREN and not attr is "can_add":
page_id_allow_list.extend(permission.page.get_children().values_list('id', flat=True))
elif permission.grant_on & MASK_DESCENDANTS:
page_id_allow_list.extend(permission.page.get_descendants().values_list('id', flat=True))

print 'descendants for pages in permissions for (' + str(user) + ') : ' + str(desc_list) # no pages with permissions has descendants???
# store value in cache

#set_permission_cache(user, attr, page_id_allow_list)
return page_id_allow_list

Expand Down
6 changes: 3 additions & 3 deletions cms/models/pagemodel.py
Expand Up @@ -752,17 +752,17 @@ def has_moderate_permission(self, request):
return False
return self.has_generic_permission(request, "moderate")

def has_generic_permission(self, request, type):
def has_generic_permission(self, request, perm_type):
"""
Return true if the current user has permission on the page.
Return the string 'All' if the user has all rights.
"""
att_name = "permission_%s_cache" % type
att_name = "permission_%s_cache" % perm_type
if not hasattr(self, "permission_user_cache") or not hasattr(self, att_name) \
or request.user.pk != self.permission_user_cache.pk:
from cms.utils.permissions import has_generic_permission
self.permission_user_cache = request.user
setattr(self, att_name, has_generic_permission(self.id, request.user, type, self.site_id))
setattr(self, att_name, has_generic_permission(self.id, request.user, perm_type, self.site_id))
if getattr(self, att_name):
self.permission_edit_cache = True

Expand Down
3 changes: 2 additions & 1 deletion cms/tests/__init__.py
Expand Up @@ -13,4 +13,5 @@
from cms.tests.reversion_tests import ReversionTestCase
from cms.tests.site import SiteTestCase
from cms.tests.urlutils import UrlutilsTestCase
from cms.tests.publisher import PublisherTestCase
from cms.tests.publisher import PublisherTestCase
from cms.tests.multilingual import MultilingualTestCase
4 changes: 2 additions & 2 deletions cms/tests/admin.py
Expand Up @@ -49,7 +49,7 @@ def test_01_edit_does_not_reset_page_adv_fields(self):
admin, normal_guy = self._get_guys()

# The admin creates the page
page = self.new_create_page(None, admin, 1, OLD_PAGE_NAME)
page = self.create_page(None, admin, 1, OLD_PAGE_NAME)
page.reverse_id = REVERSE_ID
page.save()
title = page.get_title_obj()
Expand Down Expand Up @@ -110,4 +110,4 @@ def test_01_edit_does_not_reset_page_adv_fields(self):
self.assertEqual(page.get_title(), OLD_PAGE_NAME)
self.assertEqual(page.reverse_id, REVERSE_ID)
title = page.get_title_obj()
self.assertEqual(title.overwrite_url, None)
self.assertEqual(title.overwrite_url, None)
4 changes: 2 additions & 2 deletions cms/tests/apphooks.py
Expand Up @@ -59,7 +59,7 @@ def test_03_apphook_on_root(self):

apphook_pool.clear()
superuser = User.objects.create_superuser('admin', 'admin@admin.com', 'admin')
page = self.new_create_page(user=superuser, published=True)
page = self.create_page(user=superuser, published=True)
english_title = page.title_set.all()[0]
self.assertEquals(english_title.language, 'en')
Title.objects.create(
Expand All @@ -74,4 +74,4 @@ def test_03_apphook_on_root(self):

response = self.client.get(self.get_pages_root())
self.assertTemplateUsed(response, 'sampleapp/home.html')
apphook_pool.clear()
apphook_pool.clear()
49 changes: 1 addition & 48 deletions cms/tests/base.py
Expand Up @@ -166,54 +166,7 @@ def create_page(self, parent_page=None, user=None, position="last-child",

del _thread_locals.user
return page

def new_create_page(self, parent_page=None, user=None, position="last-child",
title=None, site=1, published=False, in_navigation=False, **extra):
"""
Common way for page creation with some checks
"""
_thread_locals.user = user
language = settings.LANGUAGES[0][0]
if settings.CMS_SITE_LANGUAGES.get(site, False):
language = settings.CMS_SITE_LANGUAGES[site][0]
site = Site.objects.get(pk=site)

page_data = {
'site': site,
'template': 'nav_playground.html',
'published': published,
'in_navigation': in_navigation,
}
if user:
page_data['created_by'] = user
page_data['changed_by'] = user
if parent_page:
page_data['parent'] = parent_page
page_data.update(**extra)

page = Page.objects.create(**page_data)
if parent_page:
page.move_to(parent_page, position)
page.save()

if settings.CMS_MODERATOR and user:
page.pagemoderator_set.create(user=user)

title_data = {
'title': 'test page %d' % self.counter,
'slug': 'test-page-%d' % self.counter,
'language': language,
'page': page,
}
self.counter = self.counter + 1
if title:
title_data['title'] = title
title_data['slug'] = slugify(title)
Title.objects.create(**title_data)

del _thread_locals.user
return page

def copy_page(self, page, target_page):
from cms.utils.page import get_available_slug

Expand Down Expand Up @@ -310,4 +263,4 @@ def failUnlessWarns(self, category, message, f, *args, **kwargs):
self.assertTrue(first.category is category)

return result
assertWarns = failUnlessWarns
assertWarns = failUnlessWarns
2 changes: 1 addition & 1 deletion cms/tests/docs.py
Expand Up @@ -33,4 +33,4 @@ def test_01_html(self):
app.build()
except:
print nullout.getvalue()
raise
raise
18 changes: 9 additions & 9 deletions cms/tests/menu.py
Expand Up @@ -41,14 +41,14 @@ def create_some_nodes(self):
+ P8
"""
self.page1 = self.new_create_page(parent_page=None, published=True, in_navigation=True)
self.page2 = self.new_create_page(parent_page=self.page1, published=True, in_navigation=True)
self.page3 = self.new_create_page(parent_page=self.page2, published=True, in_navigation=True)
self.page4 = self.new_create_page(parent_page=None, published=True, in_navigation=True)
self.page5 = self.new_create_page(parent_page=self.page4, published=True, in_navigation=True)
self.page6 = self.new_create_page(parent_page=None, published=True, in_navigation=False)
self.page7 = self.new_create_page(parent_page=self.page6, published=True, in_navigation=True)
self.page8 = self.new_create_page(parent_page=self.page6, published=True, in_navigation=True)
self.page1 = self.create_page(parent_page=None, published=True, in_navigation=True)
self.page2 = self.create_page(parent_page=self.page1, published=True, in_navigation=True)
self.page3 = self.create_page(parent_page=self.page2, published=True, in_navigation=True)
self.page4 = self.create_page(parent_page=None, published=True, in_navigation=True)
self.page5 = self.create_page(parent_page=self.page4, published=True, in_navigation=True)
self.page6 = self.create_page(parent_page=None, published=True, in_navigation=False)
self.page7 = self.create_page(parent_page=self.page6, published=True, in_navigation=True)
self.page8 = self.create_page(parent_page=self.page6, published=True, in_navigation=True)
self.all_pages = [self.page1, self.page2, self.page3, self.page4,
self.page5, self.page6, self.page7, self.page8]
self.top_level_pages = [self.page1, self.page4]
Expand Down Expand Up @@ -367,7 +367,7 @@ def test_18_show_submenu_from_non_menu_page(self):
self.assertEqual(len(nodes), number_of_p7_children)

def test_19_show_breadcrumb_invisible(self):
invisible_page = self.new_create_page(parent_page=self.page3,
invisible_page = self.create_page(parent_page=self.page3,
published=True,
in_navigation=False)
context = self.get_context(path=invisible_page.get_absolute_url())
Expand Down
40 changes: 40 additions & 0 deletions cms/tests/multilingual.py
@@ -0,0 +1,40 @@
from cms.middleware.multilingual import patch_response
from django.core.urlresolvers import reverse
from django.test.testcases import TestCase
import urllib


class MultilingualTestCase(TestCase):
def test_01_multilingual_url_middleware(self):
"""
Test the Multilingual URL Middleware correctly handles the various ways
one can write URLs in HTML.
"""
# stuff we need
pages_root = urllib.unquote(reverse('pages-root'))
language = "en"
# single quoted a tag
content = "<a href='/url/'>"
output = patch_response(content, pages_root, language)
expected = "<a href='/en/url/'>"
self.assertEqual(output, expected)
# double quoted a tag
content = '<a href="/url/">'
output = patch_response(content, pages_root, language)
expected = '<a href="/en/url/">'
self.assertEqual(output, expected)
# single quoted a tag with a class and rel attribute
content = "<a rel='rel' href='/url/' class='cms'>"
output = patch_response(content, pages_root, language)
expected = "<a rel='rel' href='/en/url/' class='cms'>"
self.assertEqual(output, expected)
# single quoted form tag
content = "<form action='/url/'>"
output = patch_response(content, pages_root, language)
expected = "<form action='/en/url/'>"
self.assertEqual(output, expected)
# double quoted form tag
content = '<form action="/url/">'
output = patch_response(content, pages_root, language)
expected = '<form action="/en/url/">'
self.assertEqual(output, expected)
12 changes: 6 additions & 6 deletions cms/tests/navextender.py
Expand Up @@ -27,11 +27,11 @@ def tearDown(self):
menu_pool.menus = self.old_menu

def create_some_nodes(self):
self.page1 = self.new_create_page(parent_page=None, published=True, in_navigation=True)
self.page2 = self.new_create_page(parent_page=self.page1, published=True, in_navigation=True)
self.page3 = self.new_create_page(parent_page=self.page2, published=True, in_navigation=True)
self.page4 = self.new_create_page(parent_page=None, published=True, in_navigation=True)
self.page5 = self.new_create_page(parent_page=self.page4, published=True, in_navigation=True)
self.page1 = self.create_page(parent_page=None, published=True, in_navigation=True)
self.page2 = self.create_page(parent_page=self.page1, published=True, in_navigation=True)
self.page3 = self.create_page(parent_page=self.page2, published=True, in_navigation=True)
self.page4 = self.create_page(parent_page=None, published=True, in_navigation=True)
self.page5 = self.create_page(parent_page=self.page4, published=True, in_navigation=True)

def test_01_menu_registration(self):
self.assertEqual(len(menu_pool.menus), 2)
Expand Down Expand Up @@ -96,4 +96,4 @@ def test_05_incorrect_nav_extender_in_db(self):
nodes = context['children']
self.assertEqual(len(nodes), 2)



0 comments on commit c15be12

Please sign in to comment.