Merge lp:~cjohnston/summit/menu-fixes into lp:summit

Proposed by Chris Johnston
Status: Merged
Approved by: Michael Hall
Approved revision: 451
Merged at revision: 450
Proposed branch: lp:~cjohnston/summit/menu-fixes
Merge into: lp:summit
Diff against target: 210 lines (+130/-16)
4 files modified
summit/common/migrations/0002_update_menu_app.py (+49/-0)
summit/common/models.py (+18/-1)
summit/common/templatetags/menubuilder.py (+36/-9)
summit/common/tests.py (+27/-6)
To merge this branch: bzr merge lp:~cjohnston/summit/menu-fixes
Reviewer Review Type Date Requested Status
Michael Hall (community) Approve
Review via email: mp+127561@code.launchpad.net

Commit message

Fixes the menu highlighting

To post a comment you must log in.
Revision history for this message
Michael Hall (mhall119) wrote :

LGTM

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file 'summit/common/migrations/0002_update_menu_app.py'
2--- summit/common/migrations/0002_update_menu_app.py 1970-01-01 00:00:00 +0000
3+++ summit/common/migrations/0002_update_menu_app.py 2012-10-03 21:03:22 +0000
4@@ -0,0 +1,49 @@
5+# encoding: utf-8
6+import datetime
7+from south.db import db
8+from south.v2 import SchemaMigration
9+from django.db import models
10+
11+class Migration(SchemaMigration):
12+
13+ def forwards(self, orm):
14+
15+ # Adding field 'MenuItem.anonymous_only'
16+ db.add_column('common_menuitem', 'anonymous_only', self.gf('django.db.models.fields.BooleanField')(default=False), keep_default=False)
17+
18+
19+ def backwards(self, orm):
20+
21+ # Deleting field 'MenuItem.anonymous_only'
22+ db.delete_column('common_menuitem', 'anonymous_only')
23+
24+
25+ models = {
26+ 'common.menu': {
27+ 'Meta': {'object_name': 'Menu'},
28+ 'base_url': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
29+ 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
30+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
31+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
32+ 'site': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['sites.Site']"}),
33+ 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'db_index': 'True'})
34+ },
35+ 'common.menuitem': {
36+ 'Meta': {'object_name': 'MenuItem'},
37+ 'anonymous_only': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
38+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
39+ 'link_url': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
40+ 'login_required': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
41+ 'menu': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['common.Menu']"}),
42+ 'order': ('django.db.models.fields.IntegerField', [], {}),
43+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '100'})
44+ },
45+ 'sites.site': {
46+ 'Meta': {'ordering': "('domain',)", 'object_name': 'Site', 'db_table': "'django_site'"},
47+ 'domain': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
48+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
49+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
50+ }
51+ }
52+
53+ complete_apps = ['common']
54
55=== modified file 'summit/common/models.py'
56--- summit/common/models.py 2012-03-08 02:35:38 +0000
57+++ summit/common/models.py 2012-10-03 21:03:22 +0000
58@@ -16,12 +16,29 @@
59 def __unicode__(self):
60 return "%s" % self.name
61
62+ def save(self, force_insert=False, force_update=False):
63+ """
64+ Re-order all items from 10 upwards, at intervals of 10.
65+ This makes it easy to insert new items in the middle of
66+ existing items without having to manually shuffle
67+ them all around.
68+ """
69+ super(Menu, self).save(force_insert, force_update)
70+
71+ current = 10
72+ for item in MenuItem.objects.filter(menu=self).order_by('order'):
73+ item.order = current
74+ item.save()
75+ current += 10
76+
77+
78 class MenuItem(models.Model):
79 menu = models.ForeignKey(Menu)
80 order = models.IntegerField()
81 link_url = models.CharField(max_length=100, help_text='URL or URI to the content, eg /about/ or http://foo.com/')
82 title = models.CharField(max_length=100)
83- login_required = models.BooleanField(blank=True)
84+ login_required = models.BooleanField(blank=True, help_text='Should this item only be shown to authenticated users?')
85+ anonymous_only = models.BooleanField(blank=True, help_text='Should this item only be shown to non-logged-in users?')
86
87 def __unicode__(self):
88 return "%s %s. %s" % (self.menu.slug, self.order, self.title)
89
90=== modified file 'summit/common/templatetags/menubuilder.py'
91--- summit/common/templatetags/menubuilder.py 2012-04-17 01:53:48 +0000
92+++ summit/common/templatetags/menubuilder.py 2012-10-03 21:03:22 +0000
93@@ -1,5 +1,6 @@
94 from common.models import Menu, MenuItem
95 from django import template
96+from django.core.cache import cache
97
98 register = template.Library()
99
100@@ -23,7 +24,7 @@
101 user = context['request'].user
102 context['menuitems'] = get_items(real_menu_name, current_path, user)
103 return ''
104-
105+
106 def build_sub_menu(parser, token):
107 """
108 {% submenu %}
109@@ -49,15 +50,41 @@
110 context['submenu_items'] = context['submenu'] = None
111 return ''
112
113-def get_items(menu, current_path, user):
114- menuitems = []
115+def get_items(menu_name, current_path, user):
116+ """
117+ If possible, use a cached list of items to avoid continually re-querying
118+ the database.
119+ The key contains the menu name, whether the user is authenticated, and the current path.
120+ Disable caching by setting MENU_CACHE_TIME to -1.
121+ """
122+ from django.conf import settings
123+ cache_time = getattr(settings, 'MENU_CACHE_TIME', 1800)
124+ debug = getattr(settings, 'DEBUG', False)
125+
126+ if cache_time >= 0 and not debug:
127+ cache_key = 'django-menu-items/%s/%s/%s' % (menu_name, current_path, user.is_authenticated())
128+ menuitems = cache.get(cache_key, [])
129+ if menuitems:
130+ return menuitems
131+ else:
132+ menuitems = []
133+
134 try:
135- for i in MenuItem.objects.filter(menu__slug=menu).order_by('order'):
136- current = ( i.link_url != '/' and current_path.endswith(i.link_url + '/')) or ( i.link_url == '/' and current_path == '/' )
137- if not i.login_required or ( i.login_required and user.is_authenticated() ):
138- menuitems.append({'url': i.link_url, 'title': i.title, 'current': current,})
139- except MenuItem.DoesNotExist:
140- pass# No Menu by this name, fail gracefully
141+ menu = Menu.objects.get(slug=menu_name)
142+ except Menu.DoesNotExist:
143+ return []
144+
145+ for i in MenuItem.objects.filter(menu=menu).order_by('order'):
146+ current = ( i.link_url != '/' and current_path.startswith(i.link_url)) or ( i.link_url == '/' and current_path == '/' )
147+ if menu.base_url and i.link_url == menu.base_url and current_path != i.link_url:
148+ current = False
149+ show_anonymous = i.anonymous_only and user.is_anonymous()
150+ show_auth = i.login_required and user.is_authenticated()
151+ if (not (i.login_required or i.anonymous_only)) or (i.login_required and show_auth) or (i.anonymous_only and show_anonymous):
152+ menuitems.append({'url': i.link_url, 'title': i.title, 'current': current,})
153+
154+ if cache_time >= 0 and not debug:
155+ cache.set(cache_key, menuitems, cache_time)
156 return menuitems
157
158 register.tag('menu', build_menu)
159
160=== modified file 'summit/common/tests.py'
161--- summit/common/tests.py 2012-04-18 21:51:05 +0000
162+++ summit/common/tests.py 2012-10-03 21:03:22 +0000
163@@ -22,6 +22,8 @@
164 from summit.schedule.fields import NameField
165
166 from summit.schedule.models import Summit
167+from summit.common.models import Menu
168+from django.contrib.sites.models import Site
169
170 # Monkey-patch our NameField into the types of fields that the factory
171 # understands. This is simpler than trying to subclass the Mommy
172@@ -33,13 +35,32 @@
173 def setUp(self):
174 now = datetime.datetime.utcnow()
175 one_hour = datetime.timedelta(0, 3600)
176- summit = factory.make_one(Summit, name='uds-test')
177+ site = factory.make_one(Site, id=1)
178+ summit = factory.make_one(Summit, name='uds-test', date_start=datetime.datetime.now(), date_end=datetime.datetime.now())
179+ summit.sites.add(site)
180+ menu = factory.make_one(Menu, name='Main', slug="main", site=site)
181+ menu.menuitem_set.create(order=0, link_url='/uds-test/', title='Schedule')
182+ menu.menuitem_set.create(order=0, link_url='/today/uds-test/', title='Today')
183
184- def test_menu_highlights(self):
185+ def test_toplevel_menu_highlights(self):
186 schedule_page = self.client.get('/uds-test/')
187- self.assertContains(schedule_page, '<a class="main-nav-item current" href="/uds-test/" title="Schedule">Schedule</a>', 1)
188- self.assertContains(schedule_page, '<a class="main-nav-item current" href="/today/uds-test/" title="Today">Today</a>', 0)
189+ print schedule_page.content
190+ self.assertContains(schedule_page,
191+ '<li class=\'active\' id="main-nav"><a class="main-nav-item" href="/uds-test/" title="Schedule">Schedule</a>', 1)
192+ self.assertContains(schedule_page,
193+ '<li id="main-nav"><a class="main-nav-item" href="/uds-test/" title="Schedule">Schedule</a>', 0)
194+ self.assertContains(schedule_page,
195+ '<li class=\'active\' id="main-nav"><a class="main-nav-item" href="/today/uds-test/" title="Today">Today</a>', 0)
196+ self.assertContains(schedule_page,
197+ '<li id="main-nav"><a class="main-nav-item" href="/today/uds-test/" title="Today">Today</a>', 1)
198
199+ def test_sublevel_menu_highlights(self):
200 today_page = self.client.get('/today/uds-test/')
201- self.assertContains(today_page, '<a class="main-nav-item current" href="/uds-test/" title="Schedule">Schedule</a>', 0)
202- self.assertContains(today_page, '<a class="main-nav-item current" href="/today/uds-test/" title="Today">Today</a>', 1)
203+ self.assertContains(today_page,
204+ '<li class=\'active\' id="main-nav"><a class="main-nav-item" href="/uds-test/" title="Schedule">Schedule</a>', 0)
205+ self.assertContains(today_page,
206+ '<li id="main-nav"><a class="main-nav-item" href="/uds-test/" title="Schedule">Schedule</a>', 1)
207+ self.assertContains(today_page,
208+ '<li class=\'active\' id="main-nav"><a class="main-nav-item" href="/today/uds-test/" title="Today">Today</a>', 1)
209+ self.assertContains(today_page,
210+ '<li id="main-nav"><a class="main-nav-item" href="/today/uds-test/" title="Today">Today</a>', 0)

Subscribers

People subscribed via source and target branches