././@PaxHeader 0000000 0000000 0000000 00000000033 00000000000 010211 x ustar 00 27 mtime=1616968500.734314
django-menu-generator-ng-1.2.3/ 0000755 0001750 0001750 00000000000 00000000000 014434 5 ustar 00nik nik ././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1616885458.0
django-menu-generator-ng-1.2.3/AUTHORS.md 0000644 0001750 0001750 00000000417 00000000000 016105 0 ustar 00nik nik # Credits
## Original author:
Val Kneeman under the name [django-menuware](https://github.com/un33k/django-menuware)
## Development Lead:
* Milton Lenis - miltonln04@gmail.com
## Contributors:
* Jonathan Weth - dev@jonathanweth.de
* Dominik George - nik@naturalnet.de
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1616885458.0
django-menu-generator-ng-1.2.3/LICENSE 0000755 0001750 0001750 00000002117 00000000000 015445 0 ustar 00nik nik The MIT License
Copyright (c) Val Neekman @ Neekware Inc. http://neekware.com
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1616885458.0
django-menu-generator-ng-1.2.3/MANIFEST.in 0000644 0001750 0001750 00000000116 00000000000 016170 0 ustar 00nik nik include LICENSE
include AUTHORS.md
include README.rst
recursive-include docs * ././@PaxHeader 0000000 0000000 0000000 00000000033 00000000000 010211 x ustar 00 27 mtime=1616968500.734314
django-menu-generator-ng-1.2.3/PKG-INFO 0000644 0001750 0001750 00000001301 00000000000 015524 0 ustar 00nik nik Metadata-Version: 1.1
Name: django-menu-generator-ng
Version: 1.2.3
Summary: A straightforward menu generator for Django
Home-page: https://edugit.org/AlekSIS/libs/django-menu-generator-ng
Author: The AlekSIS Team
Author-email: aleksis-dev@lists.teckids.org
License: MIT
Description: UNKNOWN
Platform: UNKNOWN
Classifier: Development Status :: 5 - Production/Stable
Classifier: Environment :: Web Environment
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Framework :: Django
Classifier: Topic :: Utilities
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1616885458.0
django-menu-generator-ng-1.2.3/README.rst 0000644 0001750 0001750 00000015156 00000000000 016133 0 ustar 00nik nik Django Menu Generator - next generation
=======================================
A menu generating application for Django
Forked from the original at https://github.com/LaLogiaDePython/django-menu-generator
A productivity tool that enables the generation of full featured menus
through python dictionaries list, you only need to setup the HTML
structure once for each menu you like to build and then use the
dictionaries to generate menu items
Features:
---------
- Tested support to Python 3.7, 3.8, 3.9
- Tested support to Django 2.2, 3.0, 3.1
- No database
- Support unlimited menus
- Icons support
- Semi-Automatically identifies the selected item and his breadcrums
- Conditional menu items display through validators (Permissions,
Authentications or whatever you want)
Documentation:
--------------
The full documentation for Django Menu Generator is allowed here:
http://django-menu-generator.readthedocs.io/en/latest/index.html
Installation:
-------------
You can install it with one of these options:
- ``easy_install django-menu-generator``
- ``pip install django-menu-generator``
- ``git clone https://github.com/LaLogiaDePython/django-menu-generator``
1. ``cd django-menu-generator``
2. run python setup.py
- ``wget https://github.com/LaLogiaDePython/django-menu-generator/zipball/master``
1. unzip the downloaded file
2. cd into django-menu-generator-\* directory
3. run python setup.py
Usage:
------
1. Once installed, add ``'menu_generator'`` to your INSTALLED\_APPS.
2. Add ``{% load menu_generator %}`` to templates that will handle the
menus.
3. Add the list dictionaries containing the menu items to the settings
.. code:: python
####################################################################################
Example: settings.py
####################################################################################
NAV_MENU_LEFT = [
{
"name": "Home",
"url": "/",
},
{
"name": "About",
"url": "/about",
},
]
NAV_MENU_RIGHT = [
{
"name": "Login",
"url": "login_url_view", # reversible
"validators": ["menu_generator.validators.is_anonymous"],
},
{
"name": "Register",
"url": "register_view_url", # reversible
"validators": ["menu_generator.validators.is_anonymous"],
},
{
"name": "Account",
"url": "/acount",
"validators": ["menu_generator.validators.is_authenticated"],
"submenu": [
{
"name": "Profile",
"url": "/account/profile",
},
{
"name": "Account Balance",
"url": "/account/balance",
"validators": ["myapp.profiles.is_paid_user"],
},
{
"name": "Account Secrets",
"url": "/account/secrets",
"validators": ["menu_generator.validators.is_superuser"],
}
],
},
]
FOOTER_MENU_LEFT = [
{
"name": "Facebook",
"url": "facebook.com/foobar",
},
{
"name": "Contact US",
"url": "/contact",
},
]
FOOTER_MENU_RIGHT = [
{
"name": "Address",
"url": "/address",
},
]
Or you can build the menu dictionaries list inside the project apps with
``menus.py`` files, see docs for more.
4. In your template, load the template tag to generate your menu.
::
{% load menu_generator %}
Django Menu Generator
{% get_menu "NAV_MENU_LEFT" as left_menu %}
{% for item in left_menu %}
{{ item.name }}
{% if item.submenu %}
{% for menu in item.submenu %}
{% endfor %}
{% endif %}
{% endfor %}
{% get_menu "NAV_MENU_RIGHT" as right_menu %}
{% for item in right_menu %}
{{ item.name }}
{% if item.submenu %}
{% for menu in item.submenu %}
{% endfor %}
{% endif %}
{% endfor %}
{% get_menu "FOOTER_MENU_LEFT" as left_footer_menu %}
{% get_menu "FOOTER_MENU_RIGHT" as right_footer_menu %}
5. Now you must to see your menus generated when you run your project
Running the tests:
------------------
To run the tests against configured environments:
::
tox
License:
--------
Released under a (`MIT `__) license.
Author and mantainers:
----------------------
This fork is maintained by the AlekSIS team.
Original authors and maintainers
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The following people maintained django-menu-generator for a long time:
`Milton Lenis `__ - miltonln04@gmail.com
`Juan Diego García `__ - juandgoc@gmail.com
Credits:
--------
We would like to thank `Val Kneeman `__, the
original author of this project under the name 'menuware'
https://github.com/un33k/django-menuware
././@PaxHeader 0000000 0000000 0000000 00000000033 00000000000 010211 x ustar 00 27 mtime=1616968500.734314
django-menu-generator-ng-1.2.3/django_menu_generator_ng.egg-info/ 0000755 0001750 0001750 00000000000 00000000000 023146 5 ustar 00nik nik ././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1616968500.0
django-menu-generator-ng-1.2.3/django_menu_generator_ng.egg-info/PKG-INFO 0000644 0001750 0001750 00000001301 00000000000 024236 0 ustar 00nik nik Metadata-Version: 1.1
Name: django-menu-generator-ng
Version: 1.2.3
Summary: A straightforward menu generator for Django
Home-page: https://edugit.org/AlekSIS/libs/django-menu-generator-ng
Author: The AlekSIS Team
Author-email: aleksis-dev@lists.teckids.org
License: MIT
Description: UNKNOWN
Platform: UNKNOWN
Classifier: Development Status :: 5 - Production/Stable
Classifier: Environment :: Web Environment
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Framework :: Django
Classifier: Topic :: Utilities
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1616968500.0
django-menu-generator-ng-1.2.3/django_menu_generator_ng.egg-info/SOURCES.txt 0000644 0001750 0001750 00000002525 00000000000 025036 0 ustar 00nik nik AUTHORS.md
LICENSE
MANIFEST.in
README.rst
setup.cfg
setup.py
django_menu_generator_ng.egg-info/PKG-INFO
django_menu_generator_ng.egg-info/SOURCES.txt
django_menu_generator_ng.egg-info/dependency_links.txt
django_menu_generator_ng.egg-info/top_level.txt
docs/Makefile
docs/authors.rst
docs/changelog.rst
docs/conf.py
docs/contribute.rst
docs/credits.rst
docs/index.rst
docs/install.rst
docs/license.rst
docs/make.bat
docs/menugeneration.rst
docs/support.rst
docs/tests.rst
docs/urls.rst
docs/usage.rst
docs/validators.rst
menu_generator/__init__.py
menu_generator/apps.py
menu_generator/defaults.py
menu_generator/menu.py
menu_generator/utils.py
menu_generator/validators.py
menu_generator/templatetags/__init__.py
menu_generator/templatetags/menu_generator.py
menu_generator/templatetags/utils.py
menu_generator/tests/__init__.py
menu_generator/tests/test_menu.py
menu_generator/tests/test_validators.py
menu_generator/tests/testsettings.py
menu_generator/tests/urls.py
menu_generator/tests/utils.py
menu_generator/tests/test_apps/__init__.py
menu_generator/tests/test_apps/app1/__init__.py
menu_generator/tests/test_apps/app1/apps.py
menu_generator/tests/test_apps/app1/menus.py
menu_generator/tests/test_apps/app2/__init__.py
menu_generator/tests/test_apps/app2/menus.py
menu_generator/tests/test_apps/app3/__init__.py
menu_generator/tests/test_apps/app3/menus.py ././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1616968500.0
django-menu-generator-ng-1.2.3/django_menu_generator_ng.egg-info/dependency_links.txt 0000644 0001750 0001750 00000000001 00000000000 027214 0 ustar 00nik nik
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1616968500.0
django-menu-generator-ng-1.2.3/django_menu_generator_ng.egg-info/top_level.txt 0000644 0001750 0001750 00000000313 00000000000 025675 0 ustar 00nik nik menu_generator
menu_generator/templatetags
menu_generator/tests
menu_generator/tests/test_apps
menu_generator/tests/test_apps/app1
menu_generator/tests/test_apps/app2
menu_generator/tests/test_apps/app3
././@PaxHeader 0000000 0000000 0000000 00000000033 00000000000 010211 x ustar 00 27 mtime=1616968500.734314
django-menu-generator-ng-1.2.3/docs/ 0000755 0001750 0001750 00000000000 00000000000 015364 5 ustar 00nik nik ././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1616885458.0
django-menu-generator-ng-1.2.3/docs/Makefile 0000644 0001750 0001750 00000001150 00000000000 017021 0 ustar 00nik nik # Minimal makefile for Sphinx documentation
#
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = sphinx-build
SPHINXPROJ = DjangoMenuGenerator
SOURCEDIR = .
BUILDDIR = _build
# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
.PHONY: help Makefile
# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) ././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1616885458.0
django-menu-generator-ng-1.2.3/docs/authors.rst 0000644 0001750 0001750 00000000537 00000000000 017610 0 ustar 00nik nik Authors
=======
`Milton Lenis `__ - miltonln04@gmail.com
`Juan Diego García `__ - juandgoc@gmail.com
Contributors
============
`Jonathan Weth `__ - dev@jonathanweth.de
`Dominik George `__ - nik@naturalnet.de
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1616968451.0
django-menu-generator-ng-1.2.3/docs/changelog.rst 0000644 0001750 0001750 00000002565 00000000000 020055 0 ustar 00nik nik CHANGELOG
=========
1.2.3(2021-03-28)
-----------------
- Bugfix:
- Catch 404 exception if current path cannot be resolved
1.2.1(2021-01-21)
-----------------
- Features:
- Adding support for related views (Thanks to @Natureshadow)
- Project Enhancements:
- Dropping support for Python <3.7
- Dropping support for Django <2.2
- Adding support for Python 3.9
- Forked as django-menu-generator-ng due to lack of maintenance
1.1.0(2020-11-15)
-----------------
- Features:
- Adding support for newer versions of Python and Django
- Adding support for related paths (Thanks to @hansegucker)
1.0.4(2018-02-19)
-----------------
- Features:
- Adding support for AppConfig in INSTALLED_APPS
1.0.3(2018-01-31)
-----------------
- Project Enhancements:
- Dropping support for Python 2
- Updating docs
- Readme updating
- Support for Django 2.0 (Thanks to @lachmanfrantisek)
- Features:
- Adding support to root paths (Thanks to @lucaskuzma)
- BugFix:
- Fixing backward compatibility with Django < 1.10
1.0.2(2017-04-29)
-----------------
- Updating .gitignore to ignore sphinx builds
- Updating docs
1.0.1(2017-04-29)
-----------------
- Added docs
- Readme enhanced
- BUGFIX: Added a correction to the validators evaluation with the AND connector
- Added flake8 to CI
- Added tox
1.0.0 (2017-04-09)
------------------
- Initial release
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1616885458.0
django-menu-generator-ng-1.2.3/docs/conf.py 0000644 0001750 0001750 00000011440 00000000000 016663 0 ustar 00nik nik #!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# Django Menu Generator documentation build configuration file, created by
# sphinx-quickstart on Wed Apr 26 22:30:34 2017.
#
# This file is execfile()d with the current directory set to its
# containing dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
# import os
# import sys
# sys.path.insert(0, os.path.abspath('.'))
# -- General configuration ------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
#
# needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = ['sphinx.ext.coverage',
'sphinx.ext.viewcode']
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# The suffix(es) of source filenames.
# You can specify multiple suffix as a list of string:
#
# source_suffix = ['.rst', '.md']
source_suffix = '.rst'
# The master toctree document.
master_doc = 'index'
# General information about the project.
project = 'Django Menu Generator'
copyright = '2017, Milton Lenis'
author = 'Milton Lenis'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = '1.0.3'
# The full version, including alpha/beta/rc tags.
release = '1.0.3'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#
# This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases.
language = None
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This patterns also effect to html_static_path and html_extra_path
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
# If true, `todo` and `todoList` produce output, else they produce nothing.
todo_include_todos = False
# -- Options for HTML output ----------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
html_theme = 'default'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
#
# html_theme_options = {}
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
# -- Options for HTMLHelp output ------------------------------------------
# Output file base name for HTML help builder.
htmlhelp_basename = 'DjangoMenuGeneratordoc'
# -- Options for LaTeX output ---------------------------------------------
latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#
# 'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
#
# 'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#
# 'preamble': '',
# Latex figure (float) alignment
#
# 'figure_align': 'htbp',
}
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [
(master_doc, 'DjangoMenuGenerator.tex', 'Django Menu Generator Documentation',
'Milton Lenis', 'manual'),
]
# -- Options for manual page output ---------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
(master_doc, 'djangomenugenerator', 'Django Menu Generator Documentation',
[author], 1)
]
# -- Options for Texinfo output -------------------------------------------
# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
(master_doc, 'DjangoMenuGenerator', 'Django Menu Generator Documentation',
author, 'DjangoMenuGenerator', 'One line description of project.',
'Miscellaneous'),
]
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1616885458.0
django-menu-generator-ng-1.2.3/docs/contribute.rst 0000644 0001750 0001750 00000000466 00000000000 020302 0 ustar 00nik nik Contribute
===========
- Issue tracker: `https://github.com/LaLogiaDePython/django-menu-generator/issues `__
- Source code: `https://github.com/LaLogiaDePython/django-menu-generator `__
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1616885458.0
django-menu-generator-ng-1.2.3/docs/credits.rst 0000644 0001750 0001750 00000000273 00000000000 017555 0 ustar 00nik nik Credits
========
We would like to thank `Val Kneeman `__, the
original author of this project under the name 'menuware'
https://github.com/un33k/django-menuware ././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1616885458.0
django-menu-generator-ng-1.2.3/docs/index.rst 0000644 0001750 0001750 00000002166 00000000000 017232 0 ustar 00nik nik .. Django Menu Generator documentation master file, created by
sphinx-quickstart on Wed Apr 26 22:30:34 2017.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
Welcome to Django Menu Generator's documentation!
=================================================
Django Menu Generator is a productivity tool that enables the generation of full featured menus through python
dictionaries list, you only need to setup the HTML structure once for each menu you like to build and then use the
dictionaries to generate menu items
Features:
---------
- Tested support to Python 3.4, 3.5, 3.6
- Tested support to Django 1.8, 1.9, 1.10, 1.11, 2.0
- No database
- Support unlimited menus
- Icons support
- Semi-Automatically identifies the selected item and his breadcrums
- Conditional menu items display through validators (Permissions,
Authentications or whatever you want)
Contents:
---------
.. toctree::
:maxdepth: 2
install
usage
menugeneration
urls
validators
changelog
tests
authors
contribute
support
license
credits
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1616885458.0
django-menu-generator-ng-1.2.3/docs/install.rst 0000644 0001750 0001750 00000000760 00000000000 017567 0 ustar 00nik nik Installation
=============
You can install Django Menu Generator with one of these options:
- ``easy_install django-menu-generator``
- ``pip install django-menu-generator``
- ``git clone https://github.com/LaLogiaDePython/django-menu-generator``
1. ``cd django-menu-generator``
2. run python setup.py
- ``wget https://github.com/LaLogiaDePython/django-menu-generator/zipball/master``
1. unzip the downloaded file
2. cd into django-menu-generator-\* directory
3. run python setup.py ././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1616885458.0
django-menu-generator-ng-1.2.3/docs/license.rst 0000644 0001750 0001750 00000000204 00000000000 017534 0 ustar 00nik nik License
========
Released under a (`MIT `__) license. ././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1616885458.0
django-menu-generator-ng-1.2.3/docs/make.bat 0000644 0001750 0001750 00000001467 00000000000 017001 0 ustar 00nik nik @ECHO OFF
pushd %~dp0
REM Command file for Sphinx documentation
if "%SPHINXBUILD%" == "" (
set SPHINXBUILD=sphinx-build
)
set SOURCEDIR=.
set BUILDDIR=_build
set SPHINXPROJ=DjangoMenuGenerator
if "%1" == "" goto help
%SPHINXBUILD% >NUL 2>NUL
if errorlevel 9009 (
echo.
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
echo.installed, then set the SPHINXBUILD environment variable to point
echo.to the full path of the 'sphinx-build' executable. Alternatively you
echo.may add the Sphinx directory to PATH.
echo.
echo.If you don't have Sphinx installed, grab it from
echo.http://sphinx-doc.org/
exit /b 1
)
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
goto end
:help
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
:end
popd
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1616885458.0
django-menu-generator-ng-1.2.3/docs/menugeneration.rst 0000644 0001750 0001750 00000006254 00000000000 021145 0 ustar 00nik nik Menu Generation
===============
Django Menu Generator uses python dictionaries to represent the menu items, usually a menu item is as follows:
.. code:: python
{
"name": 'some name',
"icon_class": 'some icon class',
"url": URL spec,
"root": True | False,
"related_urls": [ list of related URLs ],
"related_views": [ list of related views ],
"validators": [ list of validators ],
"submenu": Dictionary like this
}
Where each key is as follows:
- ``name``: A string representing the label of the menu item. If you are using i18n here you can pass the name with the ``ugettext_lazy`` function
- ``icon_class``: A string representing the class of the icon you wish to show on the menu item, e.g you can use font-awesome
- ``url``: See :doc:`urls`
- ``related_urls``: If one of these URLs is part of the path on the currently opened page, the menu item will be marked as selected (format of URLs like described at :doc:`urls`)
- ``related_views``: If the currently opened page resolves to one of these views, the menu item will be marked as selected.
- ``root``: A flag to indicate this item is the root of a path, with this you can correctly mark nested menus as selected.
- ``validators``: See :doc:`validators`
- ``submenu``: You can create infinite nested submenus passing here menu items like this
Django Menu Generator offers two ways to generate the menus, through the Django settings and through each of the Django
apps
Generating menus through settings
---------------------------------
You can add various list dictionaries representing each menu you have as explained in :doc:`usage`
We recommend to have a ``menus.py`` file with the menu list dictionaries and then import it to the settings file if you
go this way
Generating menus through apps
-----------------------------
Some people prefer to isolate all the aspects of a project between apps, so, we add this feature to allow the menus
live inside each app.
You need to add inside the app a ``menus.py`` file that contains a dictionary called ``MENUS``, each element of the
dictionary will be a menu list dictionary with all the configuration needed to display that menu, e.g:
.. code:: python
MENUS = {
'NAV_MENU_LEFT': [
{
"name": "App1 Feature",
"url": "/app1-feature"
}
],
'NAV_MENU_TOP': [
{
"name": "Second Menu Feature",
"url": "named_url"
}
]
}
So, as an example, for the ``'NAV_MENU_LEFT'``, Django Menu Generator will loop each app searching for the ``'NAV_MENU_LEFT'``
list dictionaries inside of the ``MENUS`` and build all the menu configuration to build the whole menu.
With this feature you can have a project structure like this::
your_project/
├── config_folder/
│ └── ...
├── app1
│ └── models.py
│ forms.py
│ views.py
│ menus.py
│
├── app2
│ └── models.py
│ forms.py
│ views.py
│ menus.py
│
...
You can have a mix of the two approaches if you wish
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1616885458.0
django-menu-generator-ng-1.2.3/docs/support.rst 0000644 0001750 0001750 00000000143 00000000000 017630 0 ustar 00nik nik Support
=======
If you are having issues, please let us know.
Contact us at: miltonln04@gmail.com
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1616885458.0
django-menu-generator-ng-1.2.3/docs/tests.rst 0000644 0001750 0001750 00000000540 00000000000 017257 0 ustar 00nik nik Running the tests
==================
To run the tests against configured environments:
* Install pyenv and add all of the supported python versions there
* Add all of the version to your locals `pyenv local system 3.7.9 3.6.12 3.5.10` for example
* Install the development dependencies in `requirements_dev.txt`
* Run the tests with tox
::
tox
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1616885458.0
django-menu-generator-ng-1.2.3/docs/urls.rst 0000644 0001750 0001750 00000001443 00000000000 017105 0 ustar 00nik nik URLs
====
You can pass the URL parameters to menu items in three ways.
Raw URLs
--------
A hard-coded url:
.. code:: python
"url": '/some-path/to-feature'
Reversible URLs
---------------
An url that can be reversed with the `reverse` method:
.. code:: python
"url": 'named_url'
URL with args or kwargs
-----------------------
e.g. If you have an url with kwargs like this:
.. code:: python
url(r'^update/(?P\d+)/$', SomeView.as_view(), name='update'),
you can pass the url as follows:
.. code:: python
"url": {"viewname": 'update', "kwargs": {"pk": 1}}
In fact, you can pass any of the parameters of the reverse method through the dictionary
For Django 1.10 the reverse method sign is: ``reverse(viewname, urlconf=None, args=None, kwargs=None, current_app=None)``
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1616885458.0
django-menu-generator-ng-1.2.3/docs/usage.rst 0000644 0001750 0001750 00000010772 00000000000 017231 0 ustar 00nik nik Usage
=====
1. Once installed, add ``'menu_generator'`` to your INSTALLED\_APPS.
2. Add ``{% load menu_generator %}`` to templates that will handle the
menus.
3. Add the list dictionaries containing the menu items to the settings
.. code:: python
####################################################################################
Example: settings.py
####################################################################################
NAV_MENU_LEFT = [
{
"name": "Home",
"url": "/",
},
{
"name": "About",
"url": "/about",
},
]
NAV_MENU_RIGHT = [
{
"name": "Login",
"url": "login_url_view", # reversible
"validators": ["menu_generator.validators.is_anonymous"],
},
{
"name": "Register",
"url": "register_view_url", # reversible
"validators": ["menu_generator.validators.is_anonymous"],
},
{
"name": "Account",
"url": "/acount",
"validators": ["menu_generator.validators.is_authenticated"],
"submenu": [
{
"name": "Profile",
"url": "/account/profile",
},
{
"name": "Account Balance",
"url": "/account/balance",
"validators": ["myapp.profiles.is_paid_user"],
},
{
"name": "Account Secrets",
"url": "/account/secrets",
"validators": ["menu_generator.validators.is_superuser"],
}
],
},
]
FOOTER_MENU_LEFT = [
{
"name": "Facebook",
"url": "facebook.com/foobar",
},
{
"name": "Contact US",
"url": "/contact",
},
]
FOOTER_MENU_RIGHT = [
{
"name": "Address",
"url": "/address",
},
]
Or you can build the menu dictionaries list inside the project apps with
``menus.py`` files, see :doc:`menugeneration` for more.
4. In your template, load the template tag to generate your menu.
::
{% load menu_generator %}
Django Menu Generator
{% get_menu "NAV_MENU_LEFT" as left_menu %}
{% for item in left_menu %}
{{ item.name }}
{% if item.submenu %}
{% for menu in item.submenu %}
{% endfor %}
{% endif %}
{% endfor %}
{% get_menu "NAV_MENU_RIGHT" as right_menu %}
{% for item in right_menu %}
{{ item.name }}
{% if item.submenu %}
{% for menu in item.submenu %}
{% endfor %}
{% endif %}
{% endfor %}
{% get_menu "FOOTER_MENU_LEFT" as left_footer_menu %}
{% get_menu "FOOTER_MENU_RIGHT" as right_footer_menu %}
5. Now you must to see your menus generated when you run your project
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1616885458.0
django-menu-generator-ng-1.2.3/docs/validators.rst 0000644 0001750 0001750 00000006070 00000000000 020271 0 ustar 00nik nik Validators
==========
Django Menu Generator uses validators to allow the displaying of menu items.
A validator is a function that receives the request as arg and returns a boolean indicating if the check has passed
for Django Menu Generator the validators must always be a list containing at least one callable or python path to a callable.
If there is more than one validator, all of them will be evaluated using the AND connector.
Built-in validators
-------------------
Django Menu Generator has the following built-in validators:
- is_superuser:
A validator to check if the authenticated user is a superuser
Usage:
.. code:: python
"validators": ['menu_generator.validators.is_superuser']
- is_staff:
A validator to check if the authenticated user is member of the staff
Usage:
.. code:: python
"validators": ['menu_generator.validators.is_staff']
- is_authenticated:
A validator to check if user is authenticated
Usage:
.. code:: python
"validators": ['menu_generator.validators.is_authenticated']
- is_anonymous:
A validator to check if the user is not authenticated
Usage:
.. code:: python
"validators": ['menu_generator.validators.is_anonymous']
- user_has_permission:
A validator to check if the user has the given permission
Usage:
.. code:: python
"validators": [
('menu_generator.validators.user_has_permission', 'app_label.permission_codename')
]
- More than one validator:
You can pass more than one validator to evaluate using the AND connector
.. code:: python
"validators": [
'menu_generator.validators.is_staff',
('menu_generator.validators.user_has_permission', 'some_app.some_permission')
...
]
Custom validators
-----------------
You can build your own validators and use them with Django Menu Generator
Let's build a validator that checks if the user have more than one pet (dummy example) assuming the user has a
many to many relation called pets
Assuming we build the function inside ``your_project/app1`` on a ``menu_validators.py`` we have:
.. code:: python
# Remember you always must to past the request as first parameter
def has_more_than_one_pet(request):
return request.user.pets.count() > 0
So we can use it as a validator
.. code:: python
"validators": ['your_project.app1.menu_validators.has_more_than_one_pet']
Now let's build a validator that checks if the user's pet belongs to a specific type to illustrate the validators with
parameters.
Assuming we build the function inside the same path and the user have a foreign key called pet
.. code:: python
def has_a_pet_of_type(request, type):
return request.user.pet.type == type
So we use the validator like this:
.. code:: python
"validators": [
('your_project.app1.menu_validators.has_a_pet_of_type', 'DOG')
]
As you can see, we use tuples to pass parameters to the validators, where the first position is the validator and the rest are
the function parameters ././@PaxHeader 0000000 0000000 0000000 00000000033 00000000000 010211 x ustar 00 27 mtime=1616968500.734314
django-menu-generator-ng-1.2.3/menu_generator/ 0000755 0001750 0001750 00000000000 00000000000 017446 5 ustar 00nik nik ././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1616968457.0
django-menu-generator-ng-1.2.3/menu_generator/__init__.py 0000755 0001750 0001750 00000000254 00000000000 021563 0 ustar 00nik nik __author__ = 'Milton Lenis'
__description__ = 'A straightforward menu generator for Django'
__version__ = '1.2.3'
default_app_config = 'menu_generator.apps.MenuAppConfig'
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1616885458.0
django-menu-generator-ng-1.2.3/menu_generator/apps.py 0000755 0001750 0001750 00000000306 00000000000 020765 0 ustar 00nik nik from django.apps import AppConfig
class MenuAppConfig(AppConfig):
name = 'menu_generator'
label = 'menu_generator'
verbose_name = 'Menu Application'
def ready(self):
pass
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1616885458.0
django-menu-generator-ng-1.2.3/menu_generator/defaults.py 0000755 0001750 0001750 00000000130 00000000000 021624 0 ustar 00nik nik MENU_NOT_FOUND = [
{
"name": "MENU NOT FOUND",
"url": "/",
},
]
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1616968443.0
django-menu-generator-ng-1.2.3/menu_generator/menu.py 0000755 0001750 0001750 00000015112 00000000000 020767 0 ustar 00nik nik import copy
import django
from django.core.exceptions import ImproperlyConfigured
from django.urls import Resolver404
from .utils import get_callable, parse_url, path_startswith
if django.VERSION >= (1, 10): # pragma: no cover
from django.urls import resolve, reverse, NoReverseMatch
else:
from django.core.urlresolvers import resolve, reverse, NoReverseMatch
class MenuBase(object):
"""
Base class that generates menu list.
"""
def __init__(self):
self.path = ''
self.request = None
def save_user_state(self, request):
"""
Given a request object, store the current user attributes
:param request: HttpRequest
"""
self.request = request
self.path = request.path
def _is_validated(self, item_dict):
"""
Given a menu item dictionary, it returns true if the user passes all the validator's conditions, it means,
if the user passes all the conditions, the user can see the menu
"""
validators = item_dict.get('validators')
if not validators:
return True
if not isinstance(validators, (list, tuple)):
raise ImproperlyConfigured("validators must be a list")
result_validations = []
for validator in validators:
if isinstance(validator, tuple):
if len(validator) <= 1:
raise ImproperlyConfigured("You are passing a tuple validator without args %s" % str(validator))
func = get_callable(validator[0])
# Using a python slice to get all items after the first to build function args
args = validator[1:]
# Pass the request as first arg by default
result_validations.append(func(self.request, *args))
else:
func = get_callable(validator)
result_validations.append(func(self.request)) # pragma: no cover
return all(result_validations)
def _has_attr(self, item_dict, attr):
"""
Given a menu item dictionary, it returns true if an attr is set.
"""
if item_dict.get(attr, False):
return True
return False
def _get_icon(self, parent_dict):
"""
Given a menu item dictionary, this returns an icon class if one exist, or
returns an empty string.
"""
return parent_dict.get('icon_class', '')
def _get_url(self, item_dict):
"""
Given a menu item dictionary, it returns the URL or an empty string.
"""
url = item_dict.get('url', '')
return parse_url(url)
def _get_related_urls(self, item_dict):
"""
Given a menu item dictionary, it returns the relateds URLs or an empty list.
"""
related_urls = item_dict.get('related_urls', [])
return [parse_url(url) for url in related_urls]
def _get_related_views(self, item_dict):
"""
Given a menu item dictionary, it returns the relateds viewss or an empty list.
"""
related_views = item_dict.get('related_views', [])
return related_views
def _is_selected(self, item_dict):
"""
Given a menu item dictionary, it returns true if `url` is on path,
unless the item is marked as a root, in which case returns true if `url` is part of path.
If related URLS are given, it also returns true if one of the related URLS is part of path.
If related views are given, it also returns true if the path maps to one of these views.
"""
url = self._get_url(item_dict)
if self._is_root(item_dict) and path_startswith(self.path, url):
return True
elif url == self.path:
return True
else:
# Go through all related URLs and test
for related_url in self._get_related_urls(item_dict):
if path_startswith(self.path, related_url):
return True
# Resolve URL and check if it relates to a related views
try:
if resolve(self.path).func in self._get_related_views(item_dict):
return True
except Resolver404:
return False
return False
def _is_root(self, item_dict):
"""
Given a menu item dictionary, it returns true if item is marked as a `root`.
"""
return item_dict.get('root', False)
def _process_breadcrums(self, menu_list):
"""
Given a menu list, it marks the items on the current path as selected, which
can be used as breadcrumbs
"""
for item in menu_list:
if item['submenu']:
item['selected'] = self._process_breadcrums(item['submenu'])
if item['selected']:
return True
return False
def _get_submenu_list(self, parent_dict):
"""
Given a menu item dictionary, it returns a submenu if one exist, or
returns None.
"""
submenu = parent_dict.get('submenu', None)
if submenu:
for child_dict in submenu:
# This does a join between the menu item validators and submenu item validators and stores it on the
# submenu's validators
child_dict['validators'] = list(
set(list(parent_dict.get('validators', [])) + list(child_dict.get('validators', [])))
)
submenu = self.generate_menu(submenu)
if not submenu:
submenu = None
return submenu
def _get_menu_list(self, list_dict):
"""
A generator that returns only the visible menu items.
"""
for item in list_dict:
if self._has_attr(item, 'name') and self._has_attr(item, 'url'):
if self._is_validated(item):
yield copy.copy(item)
def generate_menu(self, list_dict):
"""
Given a list of dictionaries, returns a menu list.
"""
visible_menu = []
for item in self._get_menu_list(list_dict):
item['url'] = self._get_url(item)
item['selected'] = self._is_selected(item)
item['submenu'] = self._get_submenu_list(item)
item['icon_class'] = self._get_icon(item)
visible_menu.append(item)
self._process_breadcrums(visible_menu)
return visible_menu
class Menu(MenuBase):
"""
Class that generates menu list.
"""
def __call__(self, request, list_dict):
self.save_user_state(request)
return self.generate_menu(list_dict)
generate_menu = Menu()
././@PaxHeader 0000000 0000000 0000000 00000000033 00000000000 010211 x ustar 00 27 mtime=1616968500.734314
django-menu-generator-ng-1.2.3/menu_generator/templatetags/ 0000755 0001750 0001750 00000000000 00000000000 022140 5 ustar 00nik nik ././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1616885458.0
django-menu-generator-ng-1.2.3/menu_generator/templatetags/__init__.py 0000755 0001750 0001750 00000000000 00000000000 024242 0 ustar 00nik nik ././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1616885458.0
django-menu-generator-ng-1.2.3/menu_generator/templatetags/menu_generator.py 0000755 0001750 0001750 00000002267 00000000000 025536 0 ustar 00nik nik from django import template
from django.conf import settings
from .utils import get_menu_from_apps
from .. import defaults
from ..menu import generate_menu
register = template.Library()
@register.simple_tag(takes_context=True)
def get_menu(context, menu_name):
"""
Returns a consumable menu list for a given menu_name found in settings.py.
Else it returns an empty list.
Update, March 18 2017: Now the function get the menu list from settings and append more items if found on the
menus.py's 'MENUS' dict.
:param context: Template context
:param menu_name: String, name of the menu to be found
:return: Generated menu
"""
menu_list = getattr(settings, menu_name, defaults.MENU_NOT_FOUND)
menu_from_apps = get_menu_from_apps(menu_name)
# If there isn't a menu on settings but there is menu from apps we built menu from apps
if menu_list == defaults.MENU_NOT_FOUND and menu_from_apps:
menu_list = menu_from_apps
# It there is a menu on settings and also on apps we merge both menus
elif menu_list != defaults.MENU_NOT_FOUND and menu_from_apps:
menu_list += menu_from_apps
return generate_menu(context['request'], menu_list)
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1616885458.0
django-menu-generator-ng-1.2.3/menu_generator/templatetags/utils.py 0000644 0001750 0001750 00000001531 00000000000 023652 0 ustar 00nik nik from django.conf import settings
from ..utils import get_callable, clean_app_config
MENU_DICT = ".menus.MENUS"
def get_menu_from_apps(menu_name):
"""
Returns a consumable menulist for a given menu_name found in each menus.py file (if exists) on each app on
INSTALLED_APPS
:param menu_name: String, name of the menu to be found
:return: Consumable menu list
"""
installed_apps = getattr(settings, "INSTALLED_APPS", [])
menu_list = []
for app in installed_apps:
cleaned_app = clean_app_config(app)
try:
all_menus_dict = get_callable(cleaned_app + MENU_DICT)
except ImportError:
all_menus_dict = None
except AttributeError:
all_menus_dict = None
if all_menus_dict:
menu_list += all_menus_dict.get(menu_name, [])
return menu_list
././@PaxHeader 0000000 0000000 0000000 00000000033 00000000000 010211 x ustar 00 27 mtime=1616968500.734314
django-menu-generator-ng-1.2.3/menu_generator/tests/ 0000755 0001750 0001750 00000000000 00000000000 020610 5 ustar 00nik nik ././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1616885458.0
django-menu-generator-ng-1.2.3/menu_generator/tests/__init__.py 0000755 0001750 0001750 00000000000 00000000000 022712 0 ustar 00nik nik ././@PaxHeader 0000000 0000000 0000000 00000000033 00000000000 010211 x ustar 00 27 mtime=1616968500.734314
django-menu-generator-ng-1.2.3/menu_generator/tests/test_apps/ 0000755 0001750 0001750 00000000000 00000000000 022612 5 ustar 00nik nik ././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1616885458.0
django-menu-generator-ng-1.2.3/menu_generator/tests/test_apps/__init__.py 0000755 0001750 0001750 00000000000 00000000000 024714 0 ustar 00nik nik ././@PaxHeader 0000000 0000000 0000000 00000000033 00000000000 010211 x ustar 00 27 mtime=1616968500.734314
django-menu-generator-ng-1.2.3/menu_generator/tests/test_apps/app1/ 0000755 0001750 0001750 00000000000 00000000000 023453 5 ustar 00nik nik ././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1616885458.0
django-menu-generator-ng-1.2.3/menu_generator/tests/test_apps/app1/__init__.py 0000755 0001750 0001750 00000000055 00000000000 025567 0 ustar 00nik nik default_app_config = 'app1.apps.MyAppConfig'
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1616885458.0
django-menu-generator-ng-1.2.3/menu_generator/tests/test_apps/app1/apps.py 0000644 0001750 0001750 00000000216 00000000000 024767 0 ustar 00nik nik from django.apps import AppConfig
class MyAppConfig(AppConfig):
name = 'menu_generator.tests.test_apps.app1'
verbose_name = 'app 1'
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1616885458.0
django-menu-generator-ng-1.2.3/menu_generator/tests/test_apps/app1/menus.py 0000755 0001750 0001750 00000000371 00000000000 025160 0 ustar 00nik nik MENUS = {
'NAV_MENU': [
{
"name": "App1 Feature",
"url": "/app1-feature"
}
],
'SECOND_MENU': [
{
"name": "Second Menu Feature",
"url": "named_url"
}
]
}
././@PaxHeader 0000000 0000000 0000000 00000000033 00000000000 010211 x ustar 00 27 mtime=1616968500.734314
django-menu-generator-ng-1.2.3/menu_generator/tests/test_apps/app2/ 0000755 0001750 0001750 00000000000 00000000000 023454 5 ustar 00nik nik ././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1616885458.0
django-menu-generator-ng-1.2.3/menu_generator/tests/test_apps/app2/__init__.py 0000755 0001750 0001750 00000000000 00000000000 025556 0 ustar 00nik nik ././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1616885458.0
django-menu-generator-ng-1.2.3/menu_generator/tests/test_apps/app2/menus.py 0000755 0001750 0001750 00000000351 00000000000 025157 0 ustar 00nik nik MENUS = {
'NAV_MENU': [
{
"name": "App2 Feature",
"url": "/app2-feature"
},
{
"name": "App2 Second Feature",
"url": "/app2-second-feature"
}
]
}
././@PaxHeader 0000000 0000000 0000000 00000000033 00000000000 010211 x ustar 00 27 mtime=1616968500.734314
django-menu-generator-ng-1.2.3/menu_generator/tests/test_apps/app3/ 0000755 0001750 0001750 00000000000 00000000000 023455 5 ustar 00nik nik ././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1616885458.0
django-menu-generator-ng-1.2.3/menu_generator/tests/test_apps/app3/__init__.py 0000755 0001750 0001750 00000000000 00000000000 025557 0 ustar 00nik nik ././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1616885458.0
django-menu-generator-ng-1.2.3/menu_generator/tests/test_apps/app3/menus.py 0000755 0001750 0001750 00000000142 00000000000 025156 0 ustar 00nik nik # This file doesn't contain a 'MENUWARE_MENUS' dict with any menus, this is for coverage purposes
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1616885458.0
django-menu-generator-ng-1.2.3/menu_generator/tests/test_menu.py 0000755 0001750 0001750 00000033122 00000000000 023171 0 ustar 00nik nik from django.core.exceptions import ImproperlyConfigured
from django.http import HttpRequest
from django.test import TestCase
from .urls import testview
from .utils import TestUser, is_main_site, is_paid_user
from ..menu import MenuBase
from ..templatetags.menu_generator import get_menu
class MenuTestCase(TestCase):
"""
Menu Test
"""
def setUp(self):
"""
Setup the test.
"""
self.request = HttpRequest()
self.request.path = '/'
self.menu = MenuBase()
def test_template_tag(self):
self.request.user = TestUser(authenticated=True)
ctx = {
'request': self.request
}
nav = get_menu(ctx, 'NAV_MENU')
# While NAV_MENU is present on app1 menus and app2 menus and settings it will get 7 items
self.assertEqual(len(nav), 7)
nav = get_menu(ctx, 'SECOND_MENU')
self.assertEqual(len(nav), 1)
nav = get_menu(ctx, 'NONEXISTENT_MENU')
self.assertEqual(len(nav), 1)
self.assertEqual(nav[0]['name'], 'MENU NOT FOUND')
def test_has_attr(self):
self.assertFalse(self.menu._has_attr({}, 'name'))
self.assertFalse(self.menu._has_attr({'name': ''}, 'name'))
self.assertTrue(self.menu._has_attr({'name': 'Some Name'}, 'name'))
self.assertFalse(self.menu._has_attr({}, 'url'))
self.assertFalse(self.menu._has_attr({'url': ''}, 'url'))
self.assertTrue(self.menu._has_attr({'url': '/'}, 'url'))
self.assertTrue(self.menu._has_attr({'url': '/foo/bar'}, 'url'))
def test_get_url(self):
self.assertEqual(self.menu._get_url({}), '')
self.assertEqual(self.menu._get_url({'url': '/'}), '/')
self.assertEqual(self.menu._get_url({'url': '/foo/bar'}), '/foo/bar')
self.assertEqual(self.menu._get_url({'url': 'named_url'}), '/named-url')
params_dict = {"viewname": 'named_with_params', "kwargs": {'pk': 1}}
self.assertEqual(self.menu._get_url({'url': params_dict}), '/named-with-params/1/')
def test_state_anonymous_user(self):
self.request.user = TestUser()
self.menu.save_user_state(self.request)
self.assertFalse(self.menu.request.user.is_authenticated)
def test_state_regular_user(self):
self.request.user = TestUser(authenticated=True)
self.menu.save_user_state(self.request)
self.assertTrue(self.menu.request.user.is_authenticated)
self.assertFalse(self.menu.request.user.is_staff)
self.assertFalse(self.menu.request.user.is_superuser)
def test_state_staff(self):
self.request.user = TestUser(authenticated=True, staff=True)
self.menu.save_user_state(self.request)
self.assertTrue(self.menu.request.user.is_staff)
def test_state_superuser(self):
self.request.user = TestUser(authenticated=True, superuser=True)
self.menu.save_user_state(self.request)
self.assertTrue(self.menu.request.user.is_superuser)
def test_menu_is_validated_for_non_list_or_dict_validators(self):
menu_dict = {
"validators": "menu_generator.tests.utils.is_main_site",
}
with self.assertRaises(ImproperlyConfigured):
self.menu._is_validated(menu_dict)
def test_menu_is_validated_for_dict_validators(self):
menu_dict = {
"validators": ("menu_generator.tests.utils.is_main_site",),
}
self.assertTrue(self.menu._is_validated(menu_dict))
def test_menu_is_validated_dotted_notation(self):
menu_dict = {
"validators": ["menu_generator.tests.utils.is_main_site"],
}
self.assertTrue(self.menu._is_validated(menu_dict))
def test_menu_is_validated_invalid_dotted_notation(self):
menu_dict = {
"validators": ["foobar.hello"],
}
with self.assertRaises(ImportError):
self.menu._is_validated(menu_dict)
def test_menu_is_validated_callables(self):
menu_dict = {
"validators": [is_main_site],
}
self.assertTrue(self.menu._is_validated(menu_dict))
def test_menu_is_validated_without_validators(self):
menu_dict = {
# no validators
}
self.assertTrue(self.menu._is_validated(menu_dict))
def test_menu_is_validated_for_invalid_tuple_validator(self):
# Test case when the coder doesn't pass parameters to the validators via tuple
menu_dict = {
"validators": [("menu_generator.tests.utils.is_main_site",)]
}
self.assertRaises(ImproperlyConfigured, self.menu._is_validated, menu_dict)
def test_menu_is_validated_for_valid_tuple_validator(self):
# Test case with correct parameters.
menu_dict = {
"validators": [
("menu_generator.tests.utils.validator_with_parameters", "param1", "param2",),
"menu_generator.tests.utils.is_main_site"
]
}
self.assertTrue(self.menu._is_validated(menu_dict))
def test_menu_is_validated_for_authenticated_users(self):
menu_dict = {
"validators": ["menu_generator.validators.is_authenticated"],
}
self.request.user = TestUser(authenticated=True)
self.menu.save_user_state(self.request)
self.assertTrue(self.menu._is_validated(menu_dict))
def test_menu_is_validated_for_unauthenticated_users(self):
menu_dict = {
"validators": ["menu_generator.validators.is_anonymous"],
}
self.request.user = TestUser()
self.menu.save_user_state(self.request)
self.assertTrue(self.menu._is_validated(menu_dict))
def test_menu_is_validated_for_superusers(self):
menu_dict = {
"validators": ["menu_generator.validators.is_superuser"],
}
self.request.user = TestUser(authenticated=True, superuser=True)
self.menu.save_user_state(self.request)
self.assertTrue(self.menu._is_validated(menu_dict))
def test_menu_is_validated_for_staff(self):
menu_dict = {
"validators": ["menu_generator.validators.is_staff"],
}
self.request.user = TestUser(authenticated=True, staff=True)
self.menu.save_user_state(self.request)
self.assertTrue(self.menu._is_validated(menu_dict))
def test_menu_is_validated_for_user_with_permission(self):
menu_dict = {
"validators": [
("menu_generator.validators.user_has_permission", "test_permission",)
]
}
self.request.user = TestUser(authenticated=True)
self.menu.save_user_state(self.request)
self.assertFalse(self.menu._is_validated(menu_dict))
# Let's add the permission
self.request.user.add_perm("test_permission")
self.menu.save_user_state(self.request)
self.assertTrue(self.menu._is_validated(menu_dict))
def test_menu_is_validated_for_non_user_specific_conditions(self):
menu_dict = {
"validators": ["menu_generator.tests.utils.is_main_site"],
}
self.request.user = TestUser()
self.menu.save_user_state(self.request)
self.assertTrue(self.menu._is_validated(menu_dict))
def test_menu_multiple_validators(self):
menu_dict = {
"validators": ["menu_generator.validators.is_authenticated", "menu_generator.validators.is_staff"],
}
self.request.user = TestUser(authenticated=True)
self.menu.save_user_state(self.request)
self.assertFalse(self.menu._is_validated(menu_dict))
def test_generate_menu_submenu_attribute_inheritance(self):
self.request.user = TestUser(staff=True, authenticated=True, happy=True)
self.menu.save_user_state(self.request)
list_dict = [
{ # Menu item -- is_saff validator will be applied to the child node
"name": "parent1",
"url": "/user/account/",
"validators": ["menu_generator.validators.is_staff"],
"submenu": [
{
"name": "child1",
"url": '/user/account/profile/',
},
],
},
{ # Menu item -- is_saff validator will be applied to the child node
"name": "parent2",
"url": "/user/settings/",
"validators": ["menu_generator.validators.is_authenticated"],
"submenu": [
{
"name": "child1",
"url": '/user/settings/happy/',
"validators": [
"menu_generator.tests.utils.is_user_happy",
],
},
{
"name": "child2",
"url": '/user/settings/paid/',
"validators": [
is_paid_user,
],
},
],
},
]
nav = self.menu.generate_menu(list_dict)
self.assertEqual(len(nav), 2)
self.assertTrue('menu_generator.validators.is_staff' in nav[0]['validators'])
self.assertTrue('menu_generator.validators.is_staff' in nav[0]['submenu'][0]['validators'])
self.assertTrue('menu_generator.validators.is_authenticated' in nav[1]['validators'])
self.assertTrue('menu_generator.validators.is_authenticated' in nav[1]['submenu'][0]['validators'])
self.assertTrue('menu_generator.validators.is_authenticated' in nav[1]['submenu'][1]['validators'])
self.assertTrue('menu_generator.tests.utils.is_user_happy' in nav[1]['submenu'][0]['validators'])
self.assertTrue(is_paid_user in nav[1]['submenu'][1]['validators'])
def test_generate_menu_no_visible_submenu(self):
self.request.user = TestUser(authenticated=True)
self.menu.save_user_state(self.request)
list_dict = [
{ # Menu item -- is_saff validator will be applied to the child node
"name": "parent1",
"url": "/user/account/",
"validators": ["menu_generator.validators.is_authenticated"],
"submenu": [
{
"name": "child1",
"url": '/user/account/profile/',
"validators": ["menu_generator.validators.is_staff"] # submenu no visible
},
],
}
]
nav = self.menu.generate_menu(list_dict)
self.assertEqual(len(nav), 1)
self.assertIsNone(nav[0]["submenu"])
def test_generate_menu_selected_related_urls_simple(self):
self.request.user = TestUser(authenticated=True)
self.request.path = "/persons/1/"
self.menu.save_user_state(self.request)
list_dict = [
{
"name": "parent1",
"url": "/user/account/",
"related_urls": ["/persons/", "/groups/"],
}
]
nav = self.menu.generate_menu(list_dict)
self.assertEqual(len(nav), 1)
self.assertEqual(nav[0]["selected"], True)
def test_generate_menu_selected_related_urls_submenu(self):
self.request.user = TestUser(authenticated=True)
self.request.path = "/persons/1/"
self.menu.save_user_state(self.request)
list_dict = [
{
"name": "parent1",
"url": "/user/account/",
"submenu": [
{
"name": "child1",
"url": '/user/account/profile/',
"related_urls": ["/persons/"]
},
{
"name": "child2",
"url": 'named_url',
"related_urls": ["/groups/"]
},
],
}
]
nav = self.menu.generate_menu(list_dict)
self.assertEqual(len(nav), 1)
self.assertEqual(nav[0]["selected"], True)
self.assertEqual(nav[0]["submenu"][0]["selected"], True)
self.assertEqual(nav[0]["submenu"][1]["selected"], False)
def test_generate_menu_selected_related_views_simple(self):
self.request.user = TestUser(authenticated=True)
self.request.path = "/known-view/"
self.menu.save_user_state(self.request)
list_dict = [
{
"name": "parent1",
"url": "/user/account/",
"related_views": [testview],
}
]
nav = self.menu.generate_menu(list_dict)
self.assertEqual(len(nav), 1)
self.assertEqual(nav[0]["selected"], True)
def test_generate_menu_selected_related_views_submenu(self):
self.request.user = TestUser(authenticated=True)
self.request.path = "/known-view/"
self.menu.save_user_state(self.request)
list_dict = [
{
"name": "parent1",
"url": "/user/account/",
"submenu": [
{
"name": "child1",
"url": '/user/account/profile/',
"related_views": [testview]
},
{
"name": "child2",
"url": 'named_url',
"related_views": []
},
],
}
]
nav = self.menu.generate_menu(list_dict)
self.assertEqual(len(nav), 1)
self.assertEqual(nav[0]["selected"], True)
self.assertEqual(nav[0]["submenu"][0]["selected"], True)
self.assertEqual(nav[0]["submenu"][1]["selected"], False)
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1616885458.0
django-menu-generator-ng-1.2.3/menu_generator/tests/test_validators.py 0000755 0001750 0001750 00000002310 00000000000 024370 0 ustar 00nik nik from django.http import HttpRequest
from django.test import TestCase
from .utils import TestUser
from ..validators import is_superuser, is_staff, is_authenticated, is_anonymous, user_has_permission
class ValidatorsTestCase(TestCase):
"""
Validators test
"""
def setUp(self):
"""
Setup the test.
"""
self.request = HttpRequest()
self.request.path = '/'
def test_is_superuser(self):
self.request.user = TestUser(authenticated=True, superuser=True)
self.assertTrue(is_superuser(self.request))
def test_is_staff(self):
self.request.user = TestUser(authenticated=True, staff=True)
self.assertTrue(is_staff(self.request))
def test_is_authenticated(self):
self.request.user = TestUser(authenticated=True)
self.assertTrue(is_authenticated(self.request))
def test_is_anonymous(self):
self.request.user = TestUser()
self.assertTrue(is_anonymous(self.request))
def test_user_has_permission(self):
self.request.user = TestUser(authenticated=True)
self.request.user.add_perm("test_permission")
self.assertTrue(user_has_permission(self.request, "test_permission"))
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1616885458.0
django-menu-generator-ng-1.2.3/menu_generator/tests/testsettings.py 0000755 0001750 0001750 00000001635 00000000000 023732 0 ustar 00nik nik DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': ':memory:',
},
}
SECRET_KEY = "r4dy"
INSTALLED_APPS = [
'menu_generator',
'menu_generator.tests.test_apps.app1.apps.MyAppConfig',
'menu_generator.tests.test_apps.app2',
'menu_generator.tests.test_apps.app3'
]
MIDDLEWARE_CLASSES = []
ROOT_URLCONF = 'menu_generator.tests.urls'
NAV_MENU = [
{
"name": "Main",
"url": "/",
},
{
"name": "Account",
"url": "/account",
"validators": ["menu_generator.validators.is_authenticated", ],
"submenu": [
{
"name": "Profile",
"url": '/account/profile/',
},
],
},
{
"name": "Create User",
"url": "users:create"
},
{
"name": "Update User",
"url": {"viewname": "users:update", "kwargs": {'pk': 1}}
}
]
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1616885458.0
django-menu-generator-ng-1.2.3/menu_generator/tests/urls.py 0000755 0001750 0001750 00000000474 00000000000 022157 0 ustar 00nik nik from django.conf.urls import url
def testview(request):
return 'foo'
urlpatterns = [
url(r'named-url', lambda: 'foo', name='named_url'),
url(r'named-with-params/(?P\d+)/', lambda: 'foo', name='named_with_params'),
url(r'known-view', testview, name='known_view'),
url(r'', lambda: 'foo'),
]
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1616885458.0
django-menu-generator-ng-1.2.3/menu_generator/tests/utils.py 0000755 0001750 0001750 00000002764 00000000000 022336 0 ustar 00nik nik def is_user_happy(request):
return request.user.is_auth and request.user.is_happy # pragma: no cover
def is_paid_user(request):
return request.user.is_auth and request.user.is_happy # pragma: no cover
def is_main_site(request):
"""
Non-User condition.
"""
return True # pragma: no covere
def validator_with_parameters(request, param1, param2):
return True
class TestUser(object):
"""
Test User Object.
"""
is_auth = False
is_staff = False
is_superuser = False
is_happy = False
is_paid = False
permissions = []
def __init__(self, staff=False, superuser=False, authenticated=False, happy=False, paid=False):
self.is_auth = authenticated
self.is_staff = authenticated and staff
self.is_superuser = authenticated and superuser
self.is_happy = authenticated and happy
self.is_paid = authenticated and paid
self.permissions = []
@property
def is_authenticated(self):
return self.is_auth
def add_perm(self, permission):
"""
Method for add a permission to test user
:param permission: Permission to be added
"""
self.permissions.append(permission)
def has_perm(self, permission):
"""
Method for checking if a test user has a permission
:param permission: Permission to be checked
:return: Boolean indicating if a test user has a permission or not
"""
return permission in self.permissions
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1616885458.0
django-menu-generator-ng-1.2.3/menu_generator/utils.py 0000755 0001750 0001750 00000003526 00000000000 021171 0 ustar 00nik nik from importlib import import_module
import django
from django.apps import apps
from django.core.exceptions import ImproperlyConfigured
if django.VERSION >= (1, 10): # pragma: no cover
from django.urls import reverse, NoReverseMatch
else:
from django.core.urlresolvers import reverse, NoReverseMatch
def get_callable(func_or_path):
"""
Receives a dotted path or a callable, Returns a callable or None
"""
if callable(func_or_path):
return func_or_path
module_name = '.'.join(func_or_path.split('.')[:-1])
function_name = func_or_path.split('.')[-1]
_module = import_module(module_name)
func = getattr(_module, function_name)
return func
def clean_app_config(app_path):
"""
Removes the AppConfig path for this app and returns the new string
"""
apps_names = [app.name for app in apps.get_app_configs()]
if app_path in apps_names:
return app_path
else:
app_split = app_path.split('.')
new_app = '.'.join(app_split[:-2])
if new_app in apps_names:
return new_app
else: # pragma: no cover
raise ImproperlyConfigured(
"The application {0} is not in the configured apps or does".format(app_path) +
"not have the pattern app.apps.AppConfig"
)
def parse_url(url):
"""
Returns concrete URL for a menu dict URL attribute.
"""
try:
final_url = reverse(**url) if type(url) is dict else reverse(url)
except NoReverseMatch:
final_url = url
return final_url
def path_startswith(path, prefix):
"""
Returns True if the leftmost path components are the same as prefix.
"""
path_components = path.strip("/").split("/")
prefix_components = prefix.strip("/").split("/")
return path_components[:len(prefix_components)] == prefix_components
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1616885458.0
django-menu-generator-ng-1.2.3/menu_generator/validators.py 0000644 0001750 0001750 00000001630 00000000000 022170 0 ustar 00nik nik def is_superuser(request):
"""
Returns True if request.user is superuser else returns False
"""
return is_authenticated(request) and request.user.is_superuser
def is_staff(request):
"""
Returns True if request.user is staff else returns False
"""
return is_authenticated(request) and request.user.is_staff
def is_authenticated(request):
"""
Returns True if request.user authenticated else returns False
"""
return request.user.is_authenticated
def is_anonymous(request):
"""
Returns True if request.user is not authenticated else returns False
"""
return not request.user.is_authenticated
def user_has_permission(request, permission):
"""
Returns True if request.user has the permission else returns False
:param request: HttpRequest
:param permission: Permission to be searched
"""
return request.user.has_perm(permission)
././@PaxHeader 0000000 0000000 0000000 00000000033 00000000000 010211 x ustar 00 27 mtime=1616968500.734314
django-menu-generator-ng-1.2.3/setup.cfg 0000644 0001750 0001750 00000000201 00000000000 016246 0 ustar 00nik nik [metadata]
description-file = README.rst
[flake8]
max-line-length = 122
exclude = docs/*
[egg_info]
tag_build =
tag_date = 0
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1616885458.0
django-menu-generator-ng-1.2.3/setup.py 0000755 0001750 0001750 00000004624 00000000000 016157 0 ustar 00nik nik #!/usr/bin/env python3
from setuptools import setup
import re
import os
import sys
name = 'django-menu-generator-ng'
package = 'menu_generator'
description = "A straightforward menu generator for Django"
url = 'https://edugit.org/AlekSIS/libs/django-menu-generator-ng'
author = 'The AlekSIS Team'
author_email = 'aleksis-dev@lists.teckids.org'
keywords = "django navigation menu generator"
license = 'MIT'
install_requires = []
classifiers = [
'Development Status :: 5 - Production/Stable',
'Environment :: Web Environment',
'Intended Audience :: Developers',
'License :: OSI Approved :: MIT License',
'Operating System :: OS Independent',
'Programming Language :: Python',
"Programming Language :: Python :: 3",
"Framework :: Django",
'Topic :: Utilities'
]
def get_version(package):
"""
Return package version as listed in `__version__` in `init.py`.
"""
init_py = open(os.path.join(package, '__init__.py')).read()
return re.search("^__version__ = ['\"]([^'\"]+)['\"]", init_py, re.MULTILINE).group(1)
def get_packages(package):
"""
Return root package and all sub-packages.
"""
return [dirpath
for dirpath, dirnames, filenames in os.walk(package)
if os.path.exists(os.path.join(dirpath, '__init__.py'))]
def get_package_data(package):
"""
Return all files under the root package, that are not in a
package themselves.
"""
walk = [(dirpath.replace(package + os.sep, '', 1), filenames)
for dirpath, dirnames, filenames in os.walk(package)
if not os.path.exists(os.path.join(dirpath, '__init__.py'))]
filepaths = []
for base, filenames in walk:
filepaths.extend([os.path.join(base, filename)
for filename in filenames])
return {package: filepaths}
if sys.argv[-1] == 'publish':
os.system("python setup.py sdist upload")
args = {'version': get_version(package)}
print("You probably want to also tag the version now:")
print(" git tag -a %(version)s -m 'version %(version)s' && git push --tags" % args)
sys.exit()
setup(
name=name,
version=get_version(package),
url=url,
license=license,
description=description,
author=author,
author_email=author_email,
packages=get_packages(package),
package_data=get_package_data(package),
install_requires=install_requires,
classifiers=classifiers,
)