Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Initial import of code from my local source tree
- Loading branch information
0 parents
commit e164104
Showing
8 changed files
with
272 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
from django.contrib import admin | ||
from menu.models import Menu, MenuItem | ||
|
||
admin.site.register([Menu, MenuItem]) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
# Create your views here. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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'], | ||
) | ||
|