Skip to content

Commit

Permalink
Initial import of code from my local source tree
Browse files Browse the repository at this point in the history
  • Loading branch information
rossp committed Oct 8, 2011
0 parents commit e164104
Show file tree
Hide file tree
Showing 8 changed files with 272 additions and 0 deletions.
38 changes: 38 additions & 0 deletions README.rst
@@ -0,0 +1,38 @@
django-menu
-----------

BSD-licensed menu tools for Django, built by Ross Poulton <http://www.rossp.org>

django-menu provides a basic structure for you to build multiple navigation
menus for your website, such as the header menubar. These menus can be easily
maintained by staff using the Django administration without any knowledge
of HTML or Django internals.

Sub-menus can also be easily built and displayed only for particular URIs.

Installation & Configuration:
-----------------------------

1. `pip install django-menu`

2. Add `menu` to your `INSTALLED_APPS`

3. Add a Menu (eg called `headernavigation`) and add some items to that men

4. In your template, load the menu tags and embed your primary menu.:::

<ul>{% load menubuilder %}{% menu headernavigation %}
{% for item in menuitems %}<li><a href="{{ item.url }}" title="{{ item.title|escape }}"{% if item.current %} class='current'{% endif %}>{{ item.title }}</a></li>
{% endfor %}
</ul>


Submenus:
---------
If your template has a spot for navigation for the current sub-level of your
website tree (i.e. a dozen pages underneath `/about/`, plus a few under
`/products/`) you can create a new menu with a `URI` of `/about/`.

In your template, instead of the `{% menu %}` tag use `{% submenu %}`. If a
submenu for the current URI exists, it will be shown. The `{{ submenu_items }}`
list contains your navigation items, ready to output like in the examples above.
Empty file added menu/__init__.py
Empty file.
4 changes: 4 additions & 0 deletions menu/admin.py
@@ -0,0 +1,4 @@
from django.contrib import admin
from menu.models import Menu, MenuItem

admin.site.register([Menu, MenuItem])
35 changes: 35 additions & 0 deletions menu/models.py
@@ -0,0 +1,35 @@
from django.db import models

class Menu(models.Model):
name = models.CharField(max_length=100)
slug = models.SlugField()
base_url = models.CharField(max_length=100, blank=True, null=True)
description = models.TextField(blank=True, null=True)

def __unicode__(self):
return "%s" % self.name

def save(self, force_insert=False, force_update=False):
"""
Re-order all items at from 10 upwards, at intervals of 10.
This makes it easy to insert new items in the middle of
existing items without having to manually shuffle
them all around.
"""
super(Menu, self).save(force_insert, force_update)

current = 10
for item in MenuItem.objects.filter(menu=self).order_by('order'):
item.order = current
item.save()
current += 10

class MenuItem(models.Model):
menu = models.ForeignKey(Menu)
order = models.IntegerField()
link_url = models.CharField(max_length=100, help_text='URL or URI to the content, eg /about/ or http://foo.com/')
title = models.CharField(max_length=100)
login_required = models.BooleanField(blank=True)

def __unicode__(self):
return "%s %s. %s" % (self.menu.slug, self.order, self.title)
Empty file added menu/templatetags/__init__.py
Empty file.
60 changes: 60 additions & 0 deletions menu/templatetags/menubuilder.py
@@ -0,0 +1,60 @@
from menu.models import Menu, MenuItem
from django import template

register = template.Library()

def build_menu(parser, token):
"""
{% menu menu_name %}
"""
try:
tag_name, menu_name = token.split_contents()
except:
raise template.TemplateSyntaxError, "%r tag requires exactly one argument" % token.contents.split()[0]
return MenuObject(menu_name)

class MenuObject(template.Node):
def __init__(self, menu_name):
self.menu_name = menu_name

def render(self, context):
current_path = template.resolve_variable('request.path', context)
user = template.resolve_variable('request.user', context)
context['menuitems'] = get_items(self.menu_name, current_path, user)
return ''

def build_sub_menu(parser, token):
"""
{% submenu %}
"""
return SubMenuObject()

class SubMenuObject(template.Node):
def __init__(self):
pass

def render(self, context):
current_path = template.resolve_variable('request.path', context)
user = template.resolve_variable('request.user', context)
menu = False
for m in Menu.objects.filter(base_url__isnull=False):
if m.base_url and current_path.startswith(m.base_url):
menu = m

if menu:
context['submenu_items'] = get_items(menu.slug, current_path, user)
context['submenu'] = menu
else:
context['submenu_items'] = context['submenu'] = None
return ''

def get_items(menu, current_path, user):
menuitems = []
for i in MenuItem.objects.filter(menu__slug=menu).order_by('order'):
current = ( i.link_url != '/' and current_path.startswith(i.link_url)) or ( i.link_url == '/' and current_path == '/' )
if not i.login_required or ( i.login_required and user.is_authenticated() ):
menuitems.append({'url': i.link_url, 'title': i.title, 'current': current,})
return menuitems

register.tag('menu', build_menu)
register.tag('submenu', build_sub_menu)
1 change: 1 addition & 0 deletions menu/views.py
@@ -0,0 +1 @@
# Create your views here.
134 changes: 134 additions & 0 deletions setup.py
@@ -0,0 +1,134 @@
import os
import sys
from distutils.util import convert_path
from fnmatch import fnmatchcase
from setuptools import setup, find_packages

version = '0.1'

# Provided as an attribute, so you can append to these instead
# of replicating them:
standard_exclude = ('*.py', '*.pyc', '*$py.class', '*~', '.*', '*.bak')
standard_exclude_directories = ('.*', 'CVS', '_darcs', './build',
'./dist', 'EGG-INFO', '*.egg-info')

# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org)
# Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php
# Note: you may want to copy this into your setup.py file verbatim, as
# you can't import this from another package, when you don't know if
# that package is installed yet.
def find_package_data(
where='.', package='',
exclude=standard_exclude,
exclude_directories=standard_exclude_directories,
only_in_packages=True,
show_ignored=False):
"""
Return a dictionary suitable for use in ``package_data``
in a distutils ``setup.py`` file.
The dictionary looks like::
{'package': [files]}
Where ``files`` is a list of all the files in that package that
don't match anything in ``exclude``.
If ``only_in_packages`` is true, then top-level directories that
are not packages won't be included (but directories under packages
will).
Directories matching any pattern in ``exclude_directories`` will
be ignored; by default directories with leading ``.``, ``CVS``,
and ``_darcs`` will be ignored.
If ``show_ignored`` is true, then all the files that aren't
included in package data are shown on stderr (for debugging
purposes).
Note patterns use wildcards, or can be exact paths (including
leading ``./``), and all searching is case-insensitive.
"""

out = {}
stack = [(convert_path(where), '', package, only_in_packages)]
while stack:
where, prefix, package, only_in_packages = stack.pop(0)
for name in os.listdir(where):
fn = os.path.join(where, name)
if os.path.isdir(fn):
bad_name = False
for pattern in exclude_directories:
if (fnmatchcase(name, pattern)
or fn.lower() == pattern.lower()):
bad_name = True
if show_ignored:
print >> sys.stderr, (
"Directory %s ignored by pattern %s"
% (fn, pattern))
break
if bad_name:
continue
if (os.path.isfile(os.path.join(fn, '__init__.py'))
and not prefix):
if not package:
new_package = name
else:
new_package = package + '.' + name
stack.append((fn, '', new_package, False))
else:
stack.append((fn, prefix + name + '/', package, only_in_packages))
elif package or not only_in_packages:
# is a file
bad_name = False
for pattern in exclude:
if (fnmatchcase(name, pattern)
or fn.lower() == pattern.lower()):
bad_name = True
if show_ignored:
print >> sys.stderr, (
"File %s ignored by pattern %s"
% (fn, pattern))
break
if bad_name:
continue
out.setdefault(package, []).append(prefix+name)
return out



LONG_DESCRIPTION = """
===========
django-menu
===========
This is a Django-powered navigation management system for
basic websites.
"""

setup(
name='django-menu',
version=version,
description="Django-powered website navigation maintenance tool",
long_description=LONG_DESCRIPTION,
classifiers=[
"Programming Language :: Python",
"Topic :: Software Development :: Libraries :: Python Modules",
"Framework :: Django",
"Environment :: Web Environment",
"Operating System :: OS Independent",
"License :: OSI Approved :: BSD License",
"Natural Language :: English",
],
keywords=['django', 'menus', 'navigatino'],
author='Ross Poulton',
author_email='ross@rossp.org',
url='http://github.com/rossp/django-menu',
license='BSD',
packages=find_packages(),
package_data=find_package_data("menu", only_in_packages=False),
include_package_data=True,
zip_safe=False,
install_requires=['setuptools'],
)

0 comments on commit e164104

Please sign in to comment.