Skip to content

Commit

Permalink
Merge pull request #22 from develtech/ignore_params
Browse files Browse the repository at this point in the history
support for ignoring get params #20
  • Loading branch information
hellysmile committed Jul 25, 2017
2 parents dc0c979 + b758829 commit 41b25cc
Show file tree
Hide file tree
Showing 6 changed files with 110 additions and 29 deletions.
11 changes: 9 additions & 2 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,12 @@ The **equals** logic works best for non-hierarchical menus where only those item
</div>
{% endactiveurl %}

ignore_params ="yes|no" (default: "no")
---------------------------------------

``ignore_params`` will ignore GET parameters of URL's, e.g.
*/accounts/login/* will match */accounts/login/?next=/accounts/signup/*.

parent_tag ="div|li|self|…" (default: "li")
-------------------------------------------

Expand All @@ -182,7 +188,8 @@ The default options can be set in ``settings.py`` as well:
ACTIVE_URL_KWARGS = {
'css_class': 'active',
'parent_tag': 'li',
'menu': 'yes'
'menu': 'yes',
'ignore_params': 'no'
}
ACTIVE_URL_CACHE = True
ACTIVE_URL_CACHE_TIMEOUT = 60 * 60 * 24 # 1 day
Expand Down Expand Up @@ -225,7 +232,7 @@ Except for ``request``, options can be omitted:

.. code-block:: jinja
{% activeurl options(request, css_class="active", menu="yes", parent_tag="li") %}
{% activeurl options(request, css_class="active", menu="yes", parent_tag="li", ignore_params="no") %}
<ul>
<li>
<a href="/page/">page</a>
Expand Down
3 changes: 2 additions & 1 deletion django_activeurl/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ class ActiveUrlConf(AppConf):
KWARGS = {
'css_class': 'active',
'parent_tag': 'li',
'menu': 'yes'
'menu': 'yes',
'ignore_params': 'false',
}

class Meta:
Expand Down
3 changes: 2 additions & 1 deletion django_activeurl/ext/django_jinja.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ def render_tag(self, kwargs, caller):

# check content for "active" urls
content = render_content(
content, self.full_path, self.parent_tag, self.css_class, self.menu
content, self.full_path, self.parent_tag, self.css_class,
self.menu, self.ignore_params
)
return content
3 changes: 2 additions & 1 deletion django_activeurl/templatetags/activeurl.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ def render_tag(self, context, kwargs, nodelist):

# check content for "active" urls
content = render_content(
content, self.full_path, self.parent_tag, self.css_class, self.menu
content, self.full_path, self.parent_tag, self.css_class,
self.menu, self.ignore_params
)

return content
Expand Down
89 changes: 65 additions & 24 deletions django_activeurl/utils.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
'''template engine independent utils'''
import re
from hashlib import md5

from django.core.cache import cache
from django.utils.http import urlquote
from django.utils.translation import get_language
from django.utils.six.moves.urllib import parse as urlparse
from lxml.etree import ParserError
from lxml.html import fragment_fromstring, tostring

Expand All @@ -31,6 +31,8 @@ def load_configuration(self, kwargs):
self.parent_tag = kwargs['parent_tag']
# flipper for menu support
self.menu = kwargs['menu']
# whether to ignore / chomp get_params
self.ignore_params = kwargs['ignore_params']


def get_cache_key(content, css_class, parent_tag, full_path, menu):
Expand All @@ -52,31 +54,60 @@ def get_cache_key(content, css_class, parent_tag, full_path, menu):
return cache_key


def check_active(url, element, full_path, css_class, menu):
'''check "active" url, apply css_class'''
# django > 1.5 template boolean\None variables feature
if isinstance(menu, bool):
if menu:
menu = 'yes'
def yesno_to_bool(value, varname):
"""Return True/False from "yes"/"no".
:param value: template keyword argument value
:type value: string
:param varname: name of the variable, for use on exception raising
:type varname: string
:raises: :exc:`ImproperlyConfigured`
Django > 1.5 template boolean/None variables feature.
"""
if isinstance(value, bool):
if value:
value = 'yes'
else:
menu = 'no'
elif menu is None:
menu = 'no'
# check menu configuration, set boolean value
if menu.lower() in ('yes', 'true'):
menu = True
elif menu.lower() in ('no', 'false'):
menu = False
value = 'no'
elif value is None:
value = 'no'

# check value configuration, set boolean value
if value.lower() in ('yes', 'true'):
value = True
elif value.lower() in ('no', 'false'):
value = False
else:
raise ImproperlyConfigured('''
malformed menu value
''')
raise ImproperlyConfigured(
'activeurl: malformed param value for %s' % varname
)
return value


def check_active(url, element, full_path, css_class, menu, ignore_params):
'''check "active" url, apply css_class'''
menu = yesno_to_bool(menu, 'menu')
ignore_params = yesno_to_bool(ignore_params, 'ignore_params')

# check missing href parameter
if not url.attrib.get('href', None) is None:
# get href attribute
href = url.attrib['href'].strip()

# split into urlparse object
href = urlparse.urlsplit(href)

# cut off hashtag (anchor)
href = re.sub(r'\#.+', '', href)
href = href._replace(fragment='')

# cut off get params (?key=var&etc=var2)
if ignore_params:
href = href._replace(query='')

# build urlparse object back into string
href = urlparse.urlunsplit(href)

# check empty href
if href == '':
# replace href with current location
Expand Down Expand Up @@ -126,7 +157,9 @@ def check_active(url, element, full_path, css_class, menu):
return False


def check_content(content, full_path, css_class, parent_tag, menu):
def check_content(
content, full_path, css_class, parent_tag, menu, ignore_params
):
'''check content for "active" urls'''
# valid html root tag
try:
Expand All @@ -151,7 +184,9 @@ def check_content(content, full_path, css_class, parent_tag, menu):
urls = tree.xpath('.//a')
# check "active" status for all urls
for url in urls:
if check_active(url, url, full_path, css_class, menu):
if check_active(
url, url, full_path, css_class, menu, ignore_params
):
# mark flag for rerendering content
processed = True
# otherwise css_class must be applied to parent_tag
Expand All @@ -164,7 +199,9 @@ def check_content(content, full_path, css_class, parent_tag, menu):
urls = element.xpath('.//a')
# check "active" status for all urls
for url in urls:
if check_active(url, element, full_path, css_class, menu):
if check_active(
url, element, full_path, css_class, menu, ignore_params
):
# flag for rerendering content tree
processed = True
# stop checking other "<a>"
Expand Down Expand Up @@ -194,7 +231,9 @@ def check_content(content, full_path, css_class, parent_tag, menu):
return content


def render_content(content, full_path, parent_tag, css_class, menu):
def render_content(
content, full_path, parent_tag, css_class, menu, ignore_params
):
'''check content for "active" urls, store results to django cache'''
# try to take pre rendered content from django cache, if caching is enabled
if settings.ACTIVE_URL_CACHE:
Expand All @@ -210,7 +249,9 @@ def render_content(content, full_path, parent_tag, css_class, menu):
return from_cache

# render content with "active" logic
content = check_content(content, full_path, css_class, parent_tag, menu)
content = check_content(
content, full_path, css_class, parent_tag, menu, ignore_params
)

# write rendered content to django cache backend, if caching is enabled
if settings.ACTIVE_URL_CACHE:
Expand Down
30 changes: 30 additions & 0 deletions tests/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,36 @@ def test_hashtag():
assert not inactive_li.attrib.get('class', False)


def test_ignore_params():
template = '''
{% activeurl ignore_params='yes' %}
<ul>
<li>
<a href="/page/?heyworld=moo">page</a>
</li>
<li>
<a href="/other_page/">other_page</a>
</li>
</ul>
{% endactiveurl %}
'''

context = {'request': requests.get('/page/')}
html = render(template, context)

tree = fragment_fromstring(html)
li_elements = tree.xpath('//li')

active_li = li_elements[0]

assert active_li.attrib.get('class', False)
assert 'active' == active_li.attrib['class']

inactive_li = li_elements[1]

assert not inactive_li.attrib.get('class', False)


def test_disabled_menu_root_path():
template = '''
{% activeurl menu='no' %}
Expand Down

0 comments on commit 41b25cc

Please sign in to comment.