pax_global_header00006660000000000000000000000064126365157560014532gustar00rootroot0000000000000052 comment=c66fff26ca024197d0d16ee32b1f5630bb352687 django-sitetree-1.5.1/000077500000000000000000000000001263651575600146225ustar00rootroot00000000000000django-sitetree-1.5.1/.codeclimate.yml000066400000000000000000000003061263651575600176730ustar00rootroot00000000000000languages: JavaScript: true Python: true exclude_paths: - "sitetree/runtests.py" - "sitetree/tests.py" - "sitetree/tests/*" - "sitetree/migrations/*" - "sitetree/south_migrations/*" - "docs/*" django-sitetree-1.5.1/.coveragerc000066400000000000000000000002221263651575600167370ustar00rootroot00000000000000[run] include = sitetree/* omit = sitetree/migrations/*, sitetree/south_migrations/*, sitetree/runtests.py, sitetree/tests.py, sitetree/config.py django-sitetree-1.5.1/.gitignore000066400000000000000000000001141263651575600166060ustar00rootroot00000000000000*.pyc .project .pydevproject .idea .tox django_sitetree.egg-info docs/build django-sitetree-1.5.1/.landscape.yaml000066400000000000000000000002101263651575600175070ustar00rootroot00000000000000strictness: medium uses: - django autodetect: yes ignore-paths: - docs - sitetree/migrations - sitetree/south_migrationsdjango-sitetree-1.5.1/.travis.yml000066400000000000000000000016631263651575600167410ustar00rootroot00000000000000language: python python: - 3.5 - 3.4 - 3.3 - 2.7 - 2.6 env: - DJANGO="Django>=1.9.0,<1.10" - DJANGO="Django>=1.8.6,<1.9" - DJANGO="Django>=1.7,<1.8" - DJANGO="Django>=1.6,<1.7" - DJANGO="Django>=1.4,<1.5" install: - pip install -U coverage coveralls $DJANGO script: coverage run -a --source=sitetree sitetree/runtests.py matrix: exclude: - python: 3.5 env: DJANGO="Django>=1.7,<1.8" - python: 3.5 env: DJANGO="Django>=1.6,<1.7" - python: 3.5 env: DJANGO="Django>=1.4,<1.5" - python: 3.4 env: DJANGO="Django>=1.4,<1.5" - python: 3.3 env: DJANGO="Django>=1.9.0,<1.10" - python: 3.3 env: DJANGO="Django>=1.4,<1.5" - python: 2.6 env: DJANGO="Django>=1.9.0,<1.10" - python: 2.6 env: DJANGO="Django>=1.8.6,<1.9" - python: 2.6 env: DJANGO="Django>=1.7,<1.8" - python: 2.6 env: DJANGO="Django>=1.6,<1.7" after_success: coveralls django-sitetree-1.5.1/AUTHORS000066400000000000000000000033271263651575600156770ustar00rootroot00000000000000django-sitetree Authors ======================= Created by Igor `idle sign` Starikov. Contributors ------------ Anatoly Kudinov clincher Andrey Chibisov Vladimir Tartynskyi Arcady Usov Pavel Shiryaev Alexander Koshelev Danilo Bargen Silveron Brendtron5000 Dmitry Slepichev Arturs Vonda Jeff Triplett Jacob Kaplan-Moss Sanja Zivotic Roberto Abdelkader Scott Adams Rob Charlwood thenewguy Erika Reinhardt Dmitry Voronin Dave Pretty Alexander Artemenko ibooj Patrick Altman Ben Cole Vitaliy Ivanov Translators ----------- Russian: Igor Starikov Ukranian: Sergiy Gavrylov German: Danilo Bargen Persian: Ali Javadi Spanish: Adrián López Calvo Norwegian: Eirik Krogstad django-sitetree-1.5.1/CHANGELOG000066400000000000000000000167001263651575600160400ustar00rootroot00000000000000django-sitetree changelog ========================= v1.5.1 ------ * Django 1.9 compatibility improvements. v1.5.0 ------ + Added Norwegian translation. + Added SITETREE_RAISE_ITEMS_ERRORS_ON_DEBUG setting (see #157). + Exposed SITETREE_CACHE_TIMEOUT setting. + Added `as` clause support for `sitetree_page_title`, `sitetree_page_description` and `sitetree_page_hint` template tags. * Fixed cache problems when using sitetree_resync_apps (see #135, #166). * Fixed disappearing tree items for guests in Admin contrib (Django 1.8) (see #164). * Fix deprecation warning in Django 1.8 (see #178). * Fixed permissions check for dynamic tree items (see #165). v1.4.0 ------ + Introduced Django 1.8 support (see #152). * Fixed extra spaces issue in breadcrumbs (closes #150). v1.3.0 ------ + Implemented `django-smuggler` thirdparty support. + Implemented `i18n_patterns` compatibility (closes #148). * Fixed menu alias clash with context variable (closes #117). v1.2.1 ------- * `{{ STATIC_URL }}` is replaced with `{% static %}` admin templates. * Fixed `sitetreeload` management command compatibility with py3. * Fixed `sitetreeload` management command compatibility with Django 1.7. v1.2.0 ------- + Added support for both South and Django 1.7 migrations. + `register_dynamic_trees()` now accepts `reset_cache` kwarg. v1.1.0 ------- + Django 1.7 ready. + `item()` now accepts `access_by_perms` and `perms_mode_all`. * Fixed `SiteTreeError` for `TEMPLATE_CONTEXT_PROCESSORS` when not `DEBUG`. * Fixed `this-siblings` item alias behavior. * `register_dynamic_trees()` refactored to a less-brackets style. * `LazyTitle` objects made py3 compatible. * Fixed "Save and continue editing" on Tree Item yields "does not exist" error. * Global context is change is now tested with `id()`. v1.0.0 ------- + Added `breadcrumbs-title` template. + `UNRESOLVED_ITEM_MARKER` introduced to settings. + Added Django 1.6 test rules. * Fixed django 1.4.10 issue (see #116). - Dropped deprecated `sitetree_url` tag arguments. - Dropped deprecated template var support in sitetree item URL field. v0.12.1 ------- + Fixed bug when running sitetree with django version 1.4.10 v0.12.0 ------- + Implemented runtime defined (dynamic) trees (see #105). + Added Foundation breadcrumbs template. + Added Semantic UI templates. * Fixed `DatabaseError: integer out of range` generated on `sitetree_resync_apps` (see #105). * Sitetree for apps module name made adjustable and defaults to `sitetrees` to prevent module name clashes. v0.11.0 ------- + Implemented helpers for dynamic tree structuring (see #105) + Added experimental support for app-defined trees (see #105). + Implemented tree item access restriction "for guests only" (closes #108) + Added Bootstrap 3 templates (closes #100). v0.10.0 ------- + Added experimental support for user-defined sitetree models (see #64). + Implemented `sitetree_page_hint` template tag (closes #103). + Added Spanish translation (closes #101). v0.9.5 ------ + Added `sitetree_page_description` template tag. + Tree item dropdown experimentally exposed (see #84). + Added `this-ancestor-siblings` alias support (see #99). * Fixed 'map' object has no attribute 'append' on Py3 (see #94). * Fixed sort order changing for top level tree items (see #98). v0.9.4 ------ + Added Django 1.5 url tag syntax support for URL field. * Fixed args quoting in url tag call causing #unresolved links (closes #95). v0.9.3 ------ + Added Python 3 support (see #93). + Added persian translation. * Fixed quotes around slug-like arguments in url() (#90). * Added deprecation warnings workarounds (#89). - Dropped Django 1.3 support. v0.9.2 ------ + Django 1.3+ set as required minimum. + Added Django 1.5 support (see #87). + Added namespaces support for known urls hint (closes #83). + Added 'as' clause for sitetree_url tag (see #81). + Introduced Tree.get_title(). * Humble performance improvements for large trees (see #49). * Template variables in URL feature marked deprecated (fixes #74). v0.9.1 ------ + Added experimental URL pattern name hinting at tree item create/edit page (see #67). * Fixed tree current item detection failures with i18n trees (see #66). v0.9.0 ------ + Added support for tree and tree item admin models overriding (see #43). + Added Foundation CSS Framework menu basic templates (see #41). + Added Bootstrap CSS Framework menu and breadcrumbs basic templates (see #41). * Fixed sorting tree items by parent ID instead of parent sort order (see #55). * Fixed crashes when using sitetreeload command with colored terminal (fixes #61). v0.8.0 ------ + Added 'title' field for Tree models. + Added German translation. * Fixed management directory missing from dist (closes #50). v0.7.0 ------ + Added 'sitetreedump' and 'sitetreeload' management commands (fixes #36). + Added tests runner 'runtests.py'. Unit tests are now self-contained (see #42). + Now 'sitetree_menu' tag accepts any string as target branch alias. Related to dashed strings (see #38). + Now 'has_children' tree item attribute stands for visible children (see #38). + Now global template context is passed down all sitetree-related templates (see #39, see #40). * Fixed template tags params clashes with context variables (see #42). v0.6.0 ------ + Added i18n trees support (see #27). + Added tree items processing hook support (see #26). * Fixed various tree items filtering issues (see #34, #35). v0.5.2 ------ * Fix for yes/no icons not shown in tree admin when non-eng LANGUAGE_CODE is used (closes #31). * Fixed item's depth calculation (closes #29). * Fixed misleading tree rendering bug in admin interface. * Improved Django 1.4 compatibility (fixes #33). v0.5.1 ------ + Added Django 1.4 static files compatibility for Admin contrib. + Added support for slug-like IDs as item lookup params (see #24). * Added None check in "tree_climber" method (fixes #22). * Minor optimizations in tree.html template. v0.5.0 ------ + Now sitetree uses native Django cache framework (see #16). + Added "in_current_branch" item attribute (see #14, #18), and "current_branch" css class. * South migrations are now shipped within application package (see #19). * Current menu item now preserves "a" tag, and marked with "current_item" css class. * Request object passing forced to "menu" method (fixes #15 ). * Fixed "save & continue" wrong redirect on item's add page. * Minor fixes. v0.4.0 ------ + Added permissions calculation optimization to "get_sitetree" method (see #9). + Added item access restriction for authenticated uses only. + Added "this-ancestor-children" alias support (see #14). * Minor fixes. v0.3.1 ------ * Invalid return fix in "init_tree" method (fixes #6). * Added missing translation files. v0.3.0 ------ + New template tag "sitetree_page_title". + Added ukranian translation. + Added human-friendly debug messages. + Added permissions support (see #3). + Added South migrations. + Added "url_resolved" attribute to items. + Added basic unit tests (see # 4). + Added .rst documentation (fixed #5). * Admin menu links normalized (fixes #2). * Fixed variable names clashes in template tags file. * Missing "has_children" attribute fix. v0.2.1 ------- + Added PyPi compatibility. * Modules import fixes. * README now in .rst. v0.2.0 ------ + Added support for non-ascii in urls. v0.1.4 ------- * setup.py fix. * Django under 1.2 compatibility fix for #1. v0.1.3 ------- * "this-children" and "this-siblings" behavior fix. v0.1.2 ------- * Setting items order in admin fix. v0.1.1 ------- * Minor fixes. v0.1.0 ------ + Basic sitetree functionality. django-sitetree-1.5.1/LICENSE000066400000000000000000000030301263651575600156230ustar00rootroot00000000000000Copyright (c) 2010-2015, django-sitetree project All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the django-sitetree project nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. django-sitetree-1.5.1/MANIFEST.in000066400000000000000000000006221263651575600163600ustar00rootroot00000000000000include README.rst include AUTHORS include LICENSE include CHANGELOG include docs/Makefile recursive-include docs .rst recursive-include docs .py recursive-include sitetree/locale * recursive-include sitetree/migrations .py recursive-include sitetree/south_migrations .py recursive-include sitetree/templates .html recursive-include sitetree/templatetags .py recursive-include sitetree/management .pydjango-sitetree-1.5.1/README.rst000066400000000000000000000044721263651575600163200ustar00rootroot00000000000000django-sitetree =============== http://github.com/idlesign/django-sitetree .. image:: https://img.shields.io/pypi/v/django-sitetree.svg :target: https://pypi.python.org/pypi/django-sitetree .. image:: https://img.shields.io/pypi/dm/django-sitetree.svg :target: https://pypi.python.org/pypi/django-sitetree .. image:: https://img.shields.io/pypi/l/django-sitetree.svg :target: https://pypi.python.org/pypi/django-sitetree .. image:: https://img.shields.io/coveralls/idlesign/django-sitetree/master.svg :target: https://coveralls.io/r/idlesign/django-sitetree .. image:: https://img.shields.io/travis/idlesign/django-sitetree/master.svg :target: https://travis-ci.org/idlesign/django-sitetree .. image:: https://landscape.io/github/idlesign/django-sitetree/master/landscape.svg?style=flat :target: https://landscape.io/github/idlesign/django-sitetree/master What's that ----------- *django-sitetree is a reusable application for Django, introducing site tree, menu and breadcrumbs navigation elements.* Site structure in django-sitetree is described through Django admin interface in a so called site trees. Every item of such a tree describes a page or a set of pages through the relation of URI or URL to human-friendly title. Eg. using site tree editor in Django admin:: URI Title / - Site Root |_users/ - Site Users |_users/13/ - Definite User Alas the example above makes a little sense if you have more than just a few users, that's why django-sitetree supports Django template tags in item titles and Django named URLs in item URIs. If we define a named URL for user personal page in urls.py, for example, 'users-personal', we could change a scheme in the following way:: URI Title / - Site Root |_users/ - Site Users |_users-personal user.id - User Called {{ user.first_name }} After setting up site structure as a sitetree you should be able to use convenient and highly customizable site navigation means (menus, breadcrumbs and full site trees). User access to certain sitetree items can be restricted to authenticated users or more accurately with the help of Django permissions system (Auth contrib package). Documentation ------------- http://django-sitetree.readthedocs.org/ django-sitetree-1.5.1/docs/000077500000000000000000000000001263651575600155525ustar00rootroot00000000000000django-sitetree-1.5.1/docs/Makefile000066400000000000000000000110261263651575600172120ustar00rootroot00000000000000# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = build # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " devhelp to make HTML files and a Devhelp project" @echo " epub to make an epub" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" @echo " text to make text files" @echo " man to make manual pages" @echo " changes to make an overview of all changed/added/deprecated items" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" clean: -rm -rf $(BUILDDIR)/* html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/django-sitetree.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/django-sitetree.qhc" devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $$HOME/.local/share/devhelp/django-sitetree" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/django-sitetree" @echo "# devhelp" epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." latexpdf: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." make -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." man: $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." django-sitetree-1.5.1/docs/build/000077500000000000000000000000001263651575600166515ustar00rootroot00000000000000django-sitetree-1.5.1/docs/build/.gitignore000066400000000000000000000000001263651575600206270ustar00rootroot00000000000000django-sitetree-1.5.1/docs/source/000077500000000000000000000000001263651575600170525ustar00rootroot00000000000000django-sitetree-1.5.1/docs/source/addons.rst000066400000000000000000000005431263651575600210560ustar00rootroot00000000000000Thirdparty addons ================= Here are listed addons, helpers, tools that work in a conjunction with SiteTree. Skip through, maybe you find there something interesting. django-nav-tree --------------- Application providing a better way to set sitetree item URLs in Django Admin using popup window. https://github.com/ikresoft/django-nav-tree django-sitetree-1.5.1/docs/source/admin.rst000066400000000000000000000045131263651575600206770ustar00rootroot00000000000000Overriding SiteTree Admin representation ======================================== SiteTree allows you to override tree and tree item representation in Django Admin interface. That could be used not only for the purpose of enhancement of visual design but also for integration with other applications, using admin inlines. .. _admin-ext: The following functions from `sitetree.admin` could be used to override tree and tree item representation: * `override_tree_admin()` is used to customize tree representation. * `override_item_admin()` is used to customize tree item representation. Example: .. code-block:: python # Supposing we are in admin.py of your own application. # Import two helper functions and two admin models to inherit our custom model from. from sitetree.admin import TreeItemAdmin, TreeAdmin, override_tree_admin, override_item_admin # This is our custom tree admin model. class CustomTreeAdmin(TreeAdmin): exclude = ('title',) # Here we exclude `title` field from form. # And our custom tree item admin model. class CustomTreeItemAdmin(TreeItemAdmin): # That will turn a tree item representation from the default variant # with collapsible groupings into a flat one. fieldsets= None # Now we tell the SiteTree to replace generic representations with custom. override_tree_admin(CustomTreeAdmin) override_item_admin(CustomTreeItemAdmin) .. note:: You might also be interested in using :ref:`Tree hooks `. Inlines override example ------------------------ In the example below we'll use django-seo application from https://github.com/willhardy/django-seo According to django-seo documentation it allows an addition of custom metadata fields to your models, so we use it to connect metadata to sitetree items. That's how one might render django-seo inline form on sitetree item create and edit pages: .. code-block:: python from rollyourown.seo.admin import get_inline from sitetree.admin import TreeItemAdmin, TreeAdmin, override_tree_admin, override_item_admin # Let's suppose our application contains seo.py with django-seo metadata class defined. from myapp.seo import CustomMeta class CustomTreeItemAdmin(TreeItemAdmin): inlines = [get_inline(CustomMeta)] override_item_admin(CustomTreeItemAdmin) django-sitetree-1.5.1/docs/source/apps.rst000066400000000000000000000064001263651575600205470ustar00rootroot00000000000000Shipping sitetrees with your apps ================================= SiteTree allows you to define sitetrees within your apps. Defining a sitetree ------------------- Let's suppose you have `books` application and want do define a sitetree for it. * First create `sitetrees.py` in the directory of `books` app. * Then define a sitetree with the help of `tree` and `item` functions from `sitetree.utils` module and assign it to `sitetrees` module attribute .. code-block:: python from sitetree.utils import tree, item # Be sure you defined `sitetrees` in your module. sitetrees = ( # Define a tree with `tree` function. tree('books', items=[ # Then define items and their children with `item` function. item('Books', 'books-listing', children=[ item('Book named "{{ book.title }}"', 'books-details', in_menu=False, in_sitetree=False), item('Add a book', 'books-add'), item('Edit "{{ book.title }}"', 'books-edit', in_menu=False, in_sitetree=False) ]) ]), # ... You can define more than one tree for your app. ) Please see `tree` and `item` signatures for possible options. .. note:: If you added extra fields to the Tree and TreeItem models, then you can specify their values when instantiating `item` see :ref:`custom-model-sitetree` Export sitetree to DB --------------------- Now when your app has a defined sitetree you can use `sitetree_resync_apps` management command to instantly move sitetrees from every (or certain) applications into DB:: python manage.py sitetree_resync_apps Or solely for `books` application:: python manage.py sitetree_resync_apps books Dynamic sitetree structuring ---------------------------- Optionally you can structure app-defined sitetrees into existing or new trees runtime. Basically one should compose a dynamic tree with `compose_dynamic_tree()` and register it with `register_dynamic_trees()`. Let's suppose the following code is in `setting.py` (for Django < 1.7; or for Djagno >= 1.7 somewhere where app registry is already created, e.g. `urls.py`) of your project. .. code-block:: python from sitetree.sitetreeapp import register_dynamic_trees, compose_dynamic_tree from sitetree.utils import tree, item register_dynamic_trees( # Gather all the trees from `books`, compose_dynamic_tree('books'), # or gather all the trees from `books` and attach them to `main` tree root, compose_dynamic_tree('books', target_tree_alias='main'), # or gather all the trees from `books` and attach them to `for_books` aliased item in `main` tree, compose_dynamic_tree('books', target_tree_alias='main', parent_tree_item_alias='for_books'), # or even define a tree right at the process of registration. compose_dynamic_tree(( tree('dynamic', items=( item('dynamic_1', 'dynamic_1_url', children=( item('dynamic_1_sub_1', 'dynamic_1_sub_1_url'), )), item('dynamic_2', 'dynamic_2_url'), )), )), # Line below tells sitetree to drop and recreate cache, so that all newly registered # dynamic trees are rendered immediately. reset_cache=True ) django-sitetree-1.5.1/docs/source/conf.py000066400000000000000000000160001263651575600203460ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # django-sitetree documentation build configuration file, created by # sphinx-quickstart on Tue May 17 21:16:38 2011. # # 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. import sys, os # 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. sys.path.insert(0, os.path.abspath('../../')) from sitetree import VERSION # -- 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.autodoc'] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. #source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. project = u'django-sitetree' copyright = u'2011-2015, Igor \'idle sign\' Starikov' # 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 = '.'.join(map(str, VERSION)) # The full version, including alpha/beta/rc tags. release = '.'.join(map(str, VERSION)) # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. #language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. #today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = [] # The reST default role (used for this markup: `text`) to use for all documents. #default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. #modindex_common_prefix = [] # -- 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 themes here, relative to this directory. #html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". #html_title = None # A shorter title for the navigation bar. Default is the same as html_title. #html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. #html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. #html_favicon = None # 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'] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. #html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. #html_domain_indices = True # If false, no index is generated. #html_use_index = True # If true, the index is split into individual pages for each letter. #html_split_index = False # If true, links to the reST sources are added to the pages. #html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. #html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. #html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. #html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = 'django-sitetreedoc' # -- Options for LaTeX output -------------------------------------------------- # The paper size ('letter' or 'a4'). #latex_paper_size = 'letter' # The font size ('10pt', '11pt' or '12pt'). #latex_font_size = '10pt' # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ ('index', 'django-sitetree.tex', u'django-sitetree Documentation', u'Igor \'idle sign\' Starikov', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. #latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. #latex_use_parts = False # If true, show page references after internal links. #latex_show_pagerefs = False # If true, show URL addresses after external links. #latex_show_urls = False # Additional stuff for the LaTeX preamble. #latex_preamble = '' # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_domain_indices = True # -- Options for manual page output -------------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ ('index', 'django-sitetree', u'django-sitetree Documentation', [u'Igor \'idle sign\' Starikov'], 1) ] django-sitetree-1.5.1/docs/source/forms.rst000066400000000000000000000031731263651575600207360ustar00rootroot00000000000000SiteTree Forms and Fields ========================= Ocasionally you may want to link some site entities (e.g. Polls, Articles) to certain sitetree items (as to categorize them). You can achieve it with the help of generic forms and fields shipped with SiteTree. .. _forms: TreeItemForm ------------ You can inherit from that form to have a dropdown with tree items for a certain tree: .. code-block:: python from sitetree.forms import TreeItemForm class MyTreeItemForm(TreeItemForm): """We inherit from TreeItemForm to allow user link some title to sitetree item. This form besides `title` field will have `tree_item` dropdown. """ title = forms.CharField() # We instruct our form to work with `main` aliased sitetree. # And we set tree item with ID = 2 as initial. my_form = MyTreeItemForm(tree='main', tree_item=2) You can also use a well known `initial={'tree_item': 2}` approach to set an initial sitetree item. After that deal with that form just as usual. .. _fields: TreeItemChoiceField ------------------- `TreeItemChoiceField` is what `TreeItemForm` uses internally to represent sitetree items dropdown, and what used in Admin contrib on sitetree item create/edit pages. You can inherit from it (and customized it) or use it as it is in your own forms: .. code-block:: python from sitetree.fields import TreeItemChoiceField class MyField(TreeItemChoiceField): # We override template used to build select choices. template = 'my_templates/tree_combo.html' # And override root item representation. root_title = '-** Root item **-' django-sitetree-1.5.1/docs/source/hooks.rst000066400000000000000000000034101263651575600207250ustar00rootroot00000000000000Tree hooks ========== What to do if a time comes and you need some fancy stuff done to tree items that django-sitetree does not support? .. _tree-hooks: It might be that you need some special tree items ordering in a menu, or you want to render in a huge site tree with all articles titles that are described by one tree item in Django admin, or god knowns what else. django-sitetree can facilitate on that as it comes with ``register_items_hook(callable)`` function which registers a hook callable to process tree items right before they are passed to templates. Note that callable should be able to: a) handle ``tree_items`` and ``tree_sender`` key params. ``tree_items`` will contain a list of extended TreeItem objects ready to pass to template. ``tree_sender`` will contain navigation type identifier (e.g.: `menu`, `sitetree`, `breadcrumbs`, `menu.children`, `sitetree.children`) b) return a list of extended TreeItems objects to pass to template. Example: .. code-block:: python # First import the register function. from sitetree.sitetreeapp import register_items_hook # The following function will be used as items processor. def my_items_processor(tree_items, tree_sender): # Suppose we want to process only menu child items. if tree_sender == 'menu.children': # Lets add 'Hooked: ' to resolved titles of every item. for item in tree_items: item.title_resolved = 'Hooked: %s' % item.title_resolved # Return items list mutated or not. return tree_items # And we register items processor. register_items_hook(my_items_processor) .. note:: You might also be interested in the notes on :ref:`Overriding SiteTree Admin representation `. django-sitetree-1.5.1/docs/source/i18n.rst000066400000000000000000000031331263651575600203630ustar00rootroot00000000000000Internationalization ==================== With django-sitetree it is possible to render different trees for different active locales still addressing them by the same alias from a template. ``register_i18n_trees(aliases)`` function registers aliases of internationalized sitetrees. Internationalized sitetrees are those, which are dubbed by other trees having locale identifying suffixes in their aliases. Lets suppose ``my_tree`` is the alias of a generic tree. This tree is the one that we call by its alias in templates, and it is the one which is used if no i18n version of that tree is found. Given that ``my_tree_en``, ``my_tree_ru`` and other ``my_tree_{locale-id}``-like trees are considered internationalization sitetrees. These are used (if available) in accordance with current locale used in project. Example: .. code-block:: python # This code usually belongs to urls.py (or `ready` method of a user defined # sitetree application config if Django 1.7+). # First import the register function. from sitetree.sitetreeapp import register_i18n_trees # Now register i18n trees. register_i18n_trees(['my_tree', 'my_another_tree']) # After that you need to create trees for languages supported # in your project, e.g.: `my_tree_en`, `my_tree_ru`. # Then when we address ``my_tree`` from a template django-sitetree will render # an appropriate tree for locale currently active in your project. # See ``activate`` function from ``django.utils.translation`` # and https://docs.djangoproject.com/en/dev/topics/i18n/internationalization # for more information. django-sitetree-1.5.1/docs/source/index.rst000066400000000000000000000055151263651575600207210ustar00rootroot00000000000000django-sitetree documentation ============================= *django-sitetree is a reusable application for Django, introducing site tree, menu and breadcrumbs navigation elements.* Site structure in django-sitetree is described through Django admin interface in a so called site trees. Every item of such a tree describes a page or a set of pages through the relation of URI or URL to human-friendly title. Eg. using site tree editor in Django admin:: URI Title / - Site Root |_users/ - Site Users |_users/13/ - Definite User Alas the example above makes a little sense if you have more than just a few users, that's why django-sitetree supports Django template tags in item titles and Django named URLs in item URIs. If we define a named URL for user personal page in urls.py, for example, 'users-personal', we could change a scheme in the following way:: URI Title / - Site Root |_users/ - Site Users |_users-personal user.id - User Called {{ user.first_name }} After setting up site structure as a sitetree you should be able to use convenient and highly customizable site navigation means (menus, breadcrumbs and full site trees). User access to certain sitetree items can be restricted to authenticated users or more accurately with the help of Django permissions system (Auth contrib package). Requirements ------------ 1. Python 2.6+, 3.3+ 2. Django 1.4.2+ 3. Auth Django contrib package 4. Admin site Django contrib package (optional) 5. South 1.0+ (for automatic DB migrations; not required for Django 1.7+) Table of Contents ----------------- .. toctree:: :maxdepth: 2 quickstart tags i18n apps management templatesmod tagsadv hooks admin forms models performance addons thirdparty Get involved into django-sitetree --------------------------------- **Submit issues.** If you spotted something weird in application behavior or want to propose a feature you can do that at https://github.com/idlesign/django-sitetree/issues **Write code.** If you are eager to participate in application development, fork it at https://github.com/idlesign/django-sitetree, write your code, whether it should be a bugfix or a feature implementation, and make a pull request right from the forked project page. **Translate.** If want to translate the application into your native language use Transifex: https://www.transifex.net/projects/p/django-sitetree/. **Spread the word.** If you have some tips and tricks or any other words in mind that you think might be of interest for the others — publish it. Also ---- If the application is not what you want for site navigation, you might be interested in considering the other choices — http://djangopackages.com/grids/g/navigation/ django-sitetree-1.5.1/docs/source/management.rst000066400000000000000000000050431263651575600217220ustar00rootroot00000000000000Management commands =================== SiteTree comes with two management commands which can facilitate development and deployment processes. sitetreedump ------------ Sends sitetrees from database as a fixture in JSON format to output. Output all trees and items into `treedump.json` file example:: python manage.py sitetreedump > treedump.json You can export only trees that you need by supplying their aliases separated with spaces:: python manage.py sitetreedump my_tree my_another_tree > treedump.json If you need to export only tree items without trees use ``--items_only`` command switch:: python manage.py sitetreedump --items_only my_tree > items_only_dump.json Use ``--help`` command switch to get quick help on the command:: python manage.py sitetreedump --help sitetreeload ------------ This command loads sitetrees from a fixture in JSON format into database. .. warning:: `sitetreeload` won't even try to restore permissions for sitetree items, as those should probably be tuned in production rather than exported from dev. If required you can use Django's `loaddata` management command with `sitetreedump` created dump, or the `dumpscript` from `django-extensions` to restore the permissions. Command makes use of ``--mode`` command switch to control import strategy. a) `append` (default) mode should be used when you need to extend sitetree data that is now in DB with that from a fixture. **Note:** In this mode trees and tree items identifiers from a fixture will be changed to fit existing tree structure. b) `replace` mode should be used when you need to remove all sitetree data existing in DB and replace it with that from a fixture. **Warning:** Replacement is irreversible. You should probably dump sitetree data if you think that you might need it someday. Using `replace` mode:: python manage.py sitetreeload --mode=replace treedump.json Import all trees and items from `treedump.json` file example:: python manage.py sitetreeload treedump.json Use ``--items_into_tree`` command switch and alias of target tree to import all tree items from a fixture there. This will not respect any trees information from fixture file - only tree items will be considered. **Keep in mind** also that this switch will automatically change `sitetreeload` commmand into `append` mode:: python manage.py sitetreeload --items_into_tree=my_tree items_only_dump.json Use ``--help`` command switch to get quick help on the command:: python manage.py sitetreeload --help django-sitetree-1.5.1/docs/source/models.rst000066400000000000000000000073511263651575600210750ustar00rootroot00000000000000SiteTree Models =============== SiteTree comes with Tree and Tree item built-in models to store sitetree data. .. _models_customization: Models customization -------------------- Now let's pretend you are not satisfied with SiteTree built-in models and want to customize them. 1. First thing you should do is to define your own `tree` and `tree item` models inherited from `TreeBase` and `TreeItemBase` classes respectively: .. code-block:: python # Suppose you have `myapp` application. # In its `models.py` you define your customized models. from sitetree.models import TreeItemBase, TreeBase class MyTree(TreeBase): """This is your custom tree model. And here you add `my_tree_field` to all fields existing in `TreeBase`. """ my_tree_field = models.CharField('My tree field', max_length=50, null=True, blank=True) class MyTreeItem(TreeItemBase): """And that's a tree item model with additional `css_class` field.""" css_class = models.CharField('Tree item CSS class', max_length=50) 2. Now when `models.py` in your `myapp` application has the definitions of custom sitetree models, you need to instruct Django to use them for your project instead of built-in ones: .. code-block:: python # Somewhere in your settings.py do the following. # Here `myapp` is the name of your application, `MyTree` and `MyTreeItem` # are the names of your customized models. SITETREE_MODEL_TREE = 'myapp.MyTree' SITETREE_MODEL_TREE_ITEM = 'myapp.MyTreeItem' 3. Run `manage.py syncdb` to install your customized models into DB. .. note:: As you've added new fields to your models, you'll probably need to tune their Django Admin representation. See :ref:`Overriding SiteTree Admin representation ` for more information. .. _custom-model-sitetree: Sitetree definition with custom models -------------------------------------- Given the example model given above, you can now use the extra fields when defining a sitetree programmatically: .. code-block:: python from sitetree.utils import tree, item # Be sure you defined `sitetrees` in your module. sitetrees = ( # Define a tree with `tree` function. tree('books', items=[ # Then define items and their children with `item` function. item('Books', 'books-listing', children=[ item('Book named "{{ book.title }}"', 'books-details', in_menu=False, in_sitetree=False, css_class='book-detail'), item('Add a book', 'books-add', css_class='book-add'), item('Edit "{{ book.title }}"', 'books-edit', in_menu=False, in_sitetree=False, css_class='book-edit') ]) ]), # ... You can define more than one tree for your app. ) .. _models_referencing: Models referencing ------------------ You can reference sitetree models (including customized) from other models, with the help of `MODEL_TREE`, `MODEL_TREE_ITEM` settings: .. code-block:: python from sitetree.settings import MODEL_TREE, MODEL_TREE_ITEM # As taken from the above given examples # MODEL_TREE will contain `myapp.MyTree`, MODEL_TREE_ITEM - `myapp.MyTreeItem` If you need to get current `tree` or `tree item` classes use `get_tree_model` and `get_tree_item_model` functions: .. code-block:: python from sitetree.utils import get_tree_model, get_tree_item_model current_tree_class = get_tree_model() # MyTree from myapp.models (from the example above) current_tree_item_class = get_tree_item_model() # MyTreeItem from myapp.models (from the example above) django-sitetree-1.5.1/docs/source/performance.rst000066400000000000000000000023051263651575600221050ustar00rootroot00000000000000Performance notes ================= To avoid performance hits on large sitetrees try to simplify them, and/or reduce number of sitetree items: * Restructure (unify) sitetree items where appropriate. E.g.:: Home |-- Category "Photo" | |-- Item "{{ item.title }}" | |-- Category "Audio" | |-- Item "{{ item.title }}" | |-- etc. could be restructured into:: Home |-- Category "{{ category.title }}" | |-- Item "{{ item.title }}" | |-- etc. * Do not use ``URL as Pattern`` sitetree item option. Instead you may use hardcoded URLs. * Do not use access permissions restrictions (access rights) where not required. * Use Django templates caching machinery. * Use fast Django cache backend. .. note:: Sitetree uses Django cache framework to store trees data, but keep in mind that Django's default is `Local-memory caching `_ that is known not playing well with multiple processes (which will eventually cause sitetree to render navigation in different states for different processes), so you're advised to use the other choices. django-sitetree-1.5.1/docs/source/quickstart.rst000066400000000000000000000105341263651575600220010ustar00rootroot00000000000000Getting started =============== 1. Add the **sitetree** application to INSTALLED_APPS in your settings file (usually 'settings.py'). 2. Check that *django.core.context_processors.request* is added to TEMPLATE_CONTEXT_PROCESSORS in your settings file. For Django 1.8+: *django.template.context_processors.request* should be defined in ``TEMPLATES/OPTIONS/context_processors``. 3. Check that *django.contrib.auth.context_processors.auth* is enabled in TEMPLATE_CONTEXT_PROCESSORS too. 4. Run ``./manage.py syncdb`` to install sitetree tables into database (``./manage.py migrate`` for Django 1.7+). .. warning:: Those, who are using South <1.0 for migrations with Django <1.7, add this into settings file: .. code-block:: python SOUTH_MIGRATION_MODULES = { 'sitetree': 'sitetree.south_migrations', } 5. Go to Django Admin site and add some trees and tree items (see :ref:`Making tree ` section). 6. Add *{% load sitetree %}* tag to the top of a template. Now you can use the following template tags: + :ref:`sitetree_menu ` - to render menu based on sitetree; + :ref:`sitetree_breadcrumbs ` - to render breadcrumbs path based on sitetree; + :ref:`sitetree_tree ` - to render site tree; + :ref:`sitetree_page_title ` - to render current page title resolved against definite sitetree. + :ref:`sitetree_page_description ` - to render current page description resolved against definite sitetree. .. _making-tree: Making tree ----------- Taken from `StackOverflow `_. In this tutorial we create sitetree that could handle URI like */categoryname/entryname*. ------------ To create a tree: 1. Go to site administration panel; 2. Click +Add near 'Site Trees'; 3. Enter alias for your sitetree, e.g. 'maintree'. You'll address your tree by this alias in template tags; 4. Push 'Add Site Tree Item'; 5. Create first item:: Parent - As it is root item that would have no parent. Title - Let it be 'My site'. URL - This URL is static, so put here '/'. 6. Create second item (that one would handle 'categoryname' from your 'categoryname/entryname'):: Parent - Choose 'My site' item from step 5. Title - Put here 'Category #{{ category.id }}'. URL - Put named URL 'category-detailed category.name'. In 'Additional settings': check 'URL as Pattern' checkbox. 7. Create third item (that one would handle 'entryname' from your 'categoryname/entryname'):: Parent - Choose 'Category #{{ category.id }}' item from step 6. Title - Put here 'Entry #{{ entry.id }}'. URL - Put named URL 'entry-detailed category.name entry.name'. In 'Additional settings': check 'URL as Pattern' checkbox. 8. Put '{% load sitetree %}' into yor template to have access to sitetree tags. 9. Put '{% sitetree_menu from "maintree" include "trunk" %}' into your template to render menu from tree trunk. 10. Put '{% sitetree_breadcrumbs from "maintree" %}' into your template to render breadcrumbs. ------------ Steps 6 and 7 clarifications: * In titles we use Django template variables, which would be resolved just like they do in your templates. E.g.: You made your view for 'categoryname' (let's call it 'detailed_category') to pass category object into template as 'category' variable. Suppose that category object has 'id' property. In your template you use '{{ category.id }}' to render id. And we do just the same for site tree item in step 6. * In URLs we use Django's named URL patterns (`documentation `_). That is almost idential to usage of Django '`url `_' tag in templates. Your urls configuration for steps 6, 7 supposed to include:: url(r'^(?P\S+)/(?P\S+)/$', 'detailed_entry', name='entry-detailed'), url(r'^(?P\S+)/$', 'detailed_category', name='category-detailed'), Consider 'name' argument values of 'url' function. So, putting 'entry-detailed category.name entry.name' in step 7 into URL field we tell sitetree to associate that sitetree item with URL named 'entry-detailed', passing to it category_name and entry_name parameters. django-sitetree-1.5.1/docs/source/tags.rst000066400000000000000000000113551263651575600205470ustar00rootroot00000000000000SiteTree template tags ====================== To use template tags available in SiteTree you should add **{% load sitetree %}** tag to the top of chosen template. Tree tag argument (part in double quotes, following '**from**' word) of SiteTree tags should containt tree alias. **Hints:** + Tree tag argument could be a template variable (do not use quotes for those). + Optional **template** argument could be supplied to all SitetTree tags except *sitetree_page_title* to render using different templates. It should contain path to template file. Examples:: {% sitetree_menu from "mytree" include "trunk,topmenu" template "mytrees/mymenu.html" %} {% sitetree_breadcrumbs from "mytree" template "mytrees/mybreadcrumbs.html" %} .. _tag-menu: sitetree_menu ------------- This tag renders menu based on sitetree. Usage example:: {% sitetree_menu from "mytree" include "trunk,topmenu" %} This command renders as a menu sitetree items from tree named 'mytree', including items **under** 'trunk' and 'topmenu' aliased items. That means that 'trunk' and 'topmenu' themselves won't appear in a menu, but rather all their ancestors. If you need item filtering behaviour please use :ref:`tree hooks `. Aliases are given to items through Django's admin site. `Note that there are some reserved aliases`. To illustrate how do they work, take a look at the sample tree:: Home |-- Users | |-- Moderators | |-- Ordinary | |-- Articles | |-- About cats | | |-- Good | | |-- Bad | | |-- Ugly | | | |-- About dogs | |-- About mice | |-- Contacts | |-- Russia | | |-- Web | | | |-- Public | | | |-- Private | | | | | |-- Postal | | | |-- Australia | |-- China Exit .. note:: As it mentioned above, basic built-in templates won't limit the depth of rendered tree, if you need to render the limited number of levels, you ought to :ref:`override the built-in templates `. For brevity rendering examples below will show only top level rendered for each alias. + **trunk** - get hierarchy under trunk, i.e. root item(s) - items without parents: Renders:: Home Exit + **this-children** - get items under item resolved as current for the current page; Considering that we are now at `Articles` renders:: About cats About dogs About mice + **this-siblings** - get items under parent of item resolved as current for the current page (current item included); Considering that we are now at `Bad` renders:: Good Bad Ugly + **this-ancestor-children** - items under grandparent item (closest to root) for the item resolved as current for the current page. Considering that we are now at `Public` renders:: Web Postal Thus in the template tag example above `trunk` is reserved alias, and `topmenu` alias is given to an item through admin site. Sitetree items could be addressed not only by aliases but also by IDs:: {% sitetree_menu from "mytree" include "10" %} .. _tag-breadcrumbs: sitetree_breadcrumbs -------------------- This tag renders breadcrumbs path (from tree root to current page) based on sitetree. Usage example:: {% sitetree_breadcrumbs from "mytree" %} This command renders breadcrumbs from tree named 'mytree'. .. _tag-tree: sitetree_tree ------------- This tag renders entire site tree. Usage example:: {% sitetree_tree from "mytree" %} This command renders sitetree from tree named 'mytree'. .. _tag-page-title: sitetree_page_title ------------------- This tag renders current page title resolved against definite sitetree. Title is taken from a sitetree item title resolved as current for the current page. Usage example:: {% sitetree_page_title from "mytree" %} This command renders current page title from tree named 'mytree'. .. _tag-page-description: sitetree_page_description ------------------------- This tag renders current page description resolved against definite sitetree. Description is taken from a sitetree item description resolved as current for the current page. That can be useful for meta description for an HTML page. Usage example:: {% sitetree_page_description from "mytree" %} This command renders current page description from tree named 'mytree'. .. _tag-ignore-errors: SITETREE_RAISE_ITEMS_ERRORS_ON_DEBUG ------------------------------------ DEFAULT: True There are some rare occasions when you want to turn off errors that are thrown by sitetree even during debug. Setting SITETREE_RAISE_ITEMS_ERRORS_ON_DEBUG = False will turn them off.django-sitetree-1.5.1/docs/source/tagsadv.rst000066400000000000000000000020041263651575600212310ustar00rootroot00000000000000Advanced SiteTree tags ====================== .. _tags-advanced: SiteTree introduces two advanced template tags which you have to deal with in case you override the built-in sitetree templates. sitetree_children ----------------- Implements down the tree traversal with rendering. Usage example:: {% sitetree_children of someitem for menu template "sitetree/mychildren.html" %} Used to render child items of specific sitetree item 'someitem' for 'menu' navigation type, using template "sitetree/mychildren.html". Allowed navigation types: 1) *menu*; 2) *sitetree*. Basically template argument should contain path to current template itself. .. _tag-url: sitetree_url ------------ Resolves site tree item's url or url pattern. Usage example:: {% sitetree_url for someitem params %} This tag is much the same as Django built-in 'url' tag. The difference is that after 'for' it should get site tree item object. It can cast the resolved URL into a context variable when using `as` clause just like `url` tag. django-sitetree-1.5.1/docs/source/templatesmod.rst000066400000000000000000000142731263651575600223110ustar00rootroot00000000000000Notes on built-in templates =========================== Default templates shipped with SiteTree created to have as little markup as possible in a try to fit most common website need. Styling built-in templates -------------------------- Use CSS to style default templates for your needs. Templates are deliberately made simple, and only consist of *ul*, *li* and *a* tags. Nevertheless pay attention that menu template also uses two CSS classes marking tree items: * **current_item** — marks item in the tree, corresponding to current page; * **current_branch** — marks all ancestors of current item, and current item itself. .. _overriding-built-in-templates: Overriding built-in templates ----------------------------- To customize visual representation of navigation elements you should override the built-in SiteTree templates as follows: 1. Switch to sitetree folder 2. Switch further to 'templates/sitetree' 3. There among others you'll find the following templates: * breadcrumbs.html (basic breadcrumbs) * breadcrumbs-title.html (breadcrumbs that can be put inside html `title` tag) * menu.html (basic menu) * tree.html (basic tree) 4. Copy whichever of them you need into your project templates directory and feel free to customize it. 5. See :ref:`Advanced SiteTree tags section ` for clarification on two advanced SiteTree template tags. Templates for Foundation Framework ---------------------------------- *Information about Foundation Framework is available at* http://foundation.zurb.com The following templates are bundled with SiteTree: * `sitetree/breadcrumbs_foundation.html` This template can be used to construct a breadcrumb navigation from a sitetree. * `sitetree/menu_foundation.html` This template can be used to construct Foundation Nav Bar (classic horizontal top menu) from a sitetree. .. note:: The template renders no more than two levels of a tree with hover dropdowns for root items having children. * `sitetree/menu_foundation-vertical.html` This template can be used to construct a vertical version of Foundation Nav Bar, suitable for sidebar navigation. .. note:: The template renders no more than two levels of a tree with hover dropdowns for root items having children. * `sitetree/sitetree/menu_foundation_sidenav.html` This template can be used to construct a Foundation Side Nav. .. note:: The template renders only one tree level. You can take a look at Foundation navigation elements examples at http://foundation.zurb.com/docs/navigation.php Templates for Bootstrap Framework ------------------------------------- *Information about Bootstrap Framework is available at* http://getbootstrap.com The following templates are bundled with SiteTree: * `sitetree/breadcrumbs_bootstrap.html` This template can be used to construct a breadcrumb navigation from a sitetree. * `sitetree/breadcrumbs_bootstrap3.html` The same as above but for Bootstrap version 3. * `sitetree/menu_bootstrap.html` This template can be used to construct *menu contents* for Bootstrap Navbar. .. warning:: To widen the number of possible use-cases for which this template can be applied, it renders only menu contents, but not Navbar container itself. This means that one should wrap `sitetree_menu` call into the appropriately styled divs (i.e. having classes `navbar`, `navbar-inner`, etc.). Example:: Please see Bootstrap Navbar documentation for more information on subject. .. note:: The template renders no more than two levels of a tree with hover dropdowns for root items having children. * `sitetree/menu_bootstrap3.html` The same as above but for Bootstrap version 3. * `sitetree/menu_bootstrap_dropdown.html` One level deep dropdown menu. * `sitetree/menu_bootstrap3_dropdown.html` The same as above but for Bootstrap version 3. * `sitetree/menu_bootstrap_navlist.html` This template can be used to construct a Bootstrap Nav list. .. note:: The template renders only one tree level. * `sitetree/menu_bootstrap3_navpills.html` Constructs nav-pills Bootstrap 3 horizontal navigation. * `sitetree/menu_bootstrap3_navpills-stacked.html` Constructs nav-pills Bootstrap 3 vertical navigation similar to navlist from Bootstrap 2. You can find Bootstrap navigation elements examples at http://getbootstrap.com/components/ Templates for Semantic UI Framework -------------------------------------- *Information about Semantic UI Framework is available at* http://semantic-ui.com/ The following templates are bundled with SiteTree: * `sitetree/breadcrumbs_semantic.html` This template can be used to construct a breadcrumb navigation from a sitetree. * `sitetree/menu_semantic.html` This template can be used to construct Semantic Menu (classic horizontal top menu) from a sitetree. .. warning:: To widen the number of possible use-cases for which this template can be applied, it renders only menu contents, but not the UI Menu container itself. This means that one should wrap `sitetree_menu` call into the appropriately styled divs (i.e. having `ui menu` classes). Example:: Please see Semantic UI Menu documentation for more information on subject. .. note:: The template renders no more than two levels of a tree with hover dropdowns for root items having children. * `sitetree/menu_semantic-vertical.html` This template can be used to construct a vertical version of Semantic UI Menu, suitable for sidebar navigation. .. note:: The template renders no more than two levels of a tree with hover dropdowns for root items having children. django-sitetree-1.5.1/docs/source/thirdparty.rst000066400000000000000000000042551263651575600220040ustar00rootroot00000000000000Thirdparty applications support =============================== Here belongs some notes on thirdparty Django applications support in SiteTree. django-smuggler --------------- https://pypi.python.org/pypi/django-smuggler/ `Smuggler` dump and load buttons will be available on trees listing page if this app is installed allowing to dump and load site trees and items right from your browser. django-modeltranslation ----------------------- https://pypi.python.org/pypi/django-modeltranslation/ If you do not want to use the built-in `sitetree` Internationalization machinery, with `modeltranslation` you can localize your tree items into different languages. This requires some work though. 1. Create a custom sitetree item model: .. code-block:: python # models.py of some of your apps (e.g. myapp). from sitetree.models import TreeItemBase class MyTranslatableTreeItem(TreeItemBase): """This model will be used by modeltranslation.""" 2. Instruct Django to use your custom model: .. code-block:: python # setting.py of your project. SITETREE_MODEL_TREE_ITEM = 'myapp.MyTreeItem' 3. Tune up Admin contrib to handle translatable tree items: .. code-block:: python # admin.py of your application with translatable tree item model. from modeltranslation.admin import TranslationAdmin from sitetree.admin import TreeItemAdmin, override_item_admin class CustomTreeItemAdmin(TreeItemAdmin, TranslationAdmin): """This allows admin contrib to support translations for tree items.""" override_item_admin(CustomTreeItemAdmin) 4. Instruct `modeltranslation` how to handle your tree item model: .. code-block:: python # translation.py of your application. from modeltranslation.translator import translator, TranslationOptions from .models import MyTranslatableTreeItem class TreeItemTranslationOptions(TranslationOptions): # These fields are for translation. fields = ('title', 'hint', 'description') translator.register(MyTreeItem, TreeItemTranslationOptions) That's how you made `sitetree` work with `modeltranslation`. Read `django-modeltranslation` documentation for more information on tuning. django-sitetree-1.5.1/setup.py000077500000000000000000000024241263651575600163410ustar00rootroot00000000000000import os from setuptools import setup from sitetree import VERSION f = open(os.path.join(os.path.dirname(__file__), 'README.rst')) readme = f.read() f.close() setup( name='django-sitetree', version='.'.join(map(str, VERSION)), url='http://github.com/idlesign/django-sitetree', description='This reusable Django app introduces site tree, menu and breadcrumbs navigation elements', long_description=readme, license='BSD 3-Clause License', author='Igor `idle sign` Starikov', author_email='idlesign@yandex.ru', packages=['sitetree'], include_package_data=True, zip_safe=False, classifiers=[ 'Development Status :: 5 - Production/Stable', 'Environment :: Web Environment', 'Framework :: Django', 'Intended Audience :: Developers', 'License :: OSI Approved :: BSD License', 'Operating System :: OS Independent', 'Programming Language :: Python', 'Programming Language :: Python :: 2', 'Programming Language :: Python :: 2.6', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.3', 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', ], ) django-sitetree-1.5.1/sitetree/000077500000000000000000000000001263651575600164465ustar00rootroot00000000000000django-sitetree-1.5.1/sitetree/__init__.py000066400000000000000000000001141263651575600205530ustar00rootroot00000000000000VERSION = (1, 5, 1) default_app_config = 'sitetree.config.SitetreeConfig' django-sitetree-1.5.1/sitetree/admin.py000066400000000000000000000320031263651575600201060ustar00rootroot00000000000000from django.conf import settings as django_settings from django import VERSION as django_version from django.core.urlresolvers import get_urlconf, get_resolver from django.utils.translation import ugettext_lazy as _ from django.utils import six from django.http import HttpResponseRedirect from django.contrib import admin from django.contrib.admin.sites import NotRegistered from django.contrib import messages from django.conf.urls import url DJANGO_POST_19 = django_version >= (1, 9, 0) if not DJANGO_POST_19: from django.conf.urls import patterns as patterns_func from .settings import MODEL_TREE, MODEL_TREE_ITEM from .fields import TreeItemChoiceField from .utils import get_tree_model, get_tree_item_model, get_app_n_model SMUGGLER_INSTALLED = 'smuggler' in django_settings.INSTALLED_APPS MODEL_TREE_CLASS = get_tree_model() MODEL_TREE_ITEM_CLASS = get_tree_item_model() _TREE_ADMIN = lambda: TreeAdmin _ITEM_ADMIN = lambda: TreeItemAdmin def get_model_url_name(model_nfo, page, with_namespace=False): """Returns a URL for a given Tree admin page type.""" prefix = '' if with_namespace: prefix = 'admin:' return ('%s%s_%s' % (prefix, '%s_%s' % model_nfo, page)).lower() def get_tree_url_name(page, with_namespace=False): """Returns a URL for a given Tree admin page type.""" return get_model_url_name(get_app_n_model('MODEL_TREE'), page, with_namespace) def get_tree_item_url_name(page, with_namespace=False): """Returns a URL for a given Tree Item admin page type.""" return get_model_url_name(get_app_n_model('MODEL_TREE_ITEM'), page, with_namespace) _TREE_URLS = { 'app': get_app_n_model('MODEL_TREE')[0], 'change': get_tree_url_name('change', True), 'changelist': get_tree_url_name('changelist', True), 'treeitem_change': get_tree_item_url_name('change', True) } def _reregister_tree_admin(): """Forces unregistration of tree admin class with following re-registration.""" try: admin.site.unregister(MODEL_TREE_CLASS) except NotRegistered: pass admin.site.register(MODEL_TREE_CLASS, _TREE_ADMIN()) def override_tree_admin(admin_class): """Sets a class that should be used instead of TreeAdmin to represent trees in the Admin interface. Note that the class must inherit from TreeAdmin. """ global _TREE_ADMIN _TREE_ADMIN = lambda: admin_class _reregister_tree_admin() def override_item_admin(admin_class): """Sets a class that should be used instead of TreeItemAdmin to represent tree items in the Admin interface. Note that the class must inherit from TreeItemAdmin. """ global _ITEM_ADMIN _ITEM_ADMIN = lambda: admin_class _reregister_tree_admin() class TreeItemAdmin(admin.ModelAdmin): exclude = ('tree', 'sort_order') fieldsets = ( (_('Basic settings'), { 'fields': ('parent', 'title', 'url',) }), (_('Access settings'), { 'classes': ('collapse',), 'fields': ('access_loggedin', 'access_guest', 'access_restricted', 'access_permissions', 'access_perm_type') }), (_('Display settings'), { 'classes': ('collapse',), 'fields': ('hidden', 'inmenu', 'inbreadcrumbs', 'insitetree') }), (_('Additional settings'), { 'classes': ('collapse',), 'fields': ('hint', 'description', 'alias', 'urlaspattern') }), ) filter_horizontal = ('access_permissions',) change_form_template = 'admin/sitetree/treeitem/change_form.html' def response_add(self, request, obj, post_url_continue=None, **kwargs): """Redirects to the appropriate items' 'continue' page on item add. As we administer tree items within tree itself, we should make some changes to redirection process. """ if post_url_continue is None: post_url_continue = '../item_%s/' % obj.pk return super(TreeItemAdmin, self).response_add(request, obj, post_url_continue) def response_change(self, request, obj, **kwargs): """Redirects to the appropriate items' 'add' page on item change. As we administer tree items within tree itself, we should make some changes to redirection process. """ response = super(TreeItemAdmin, self).response_change(request, obj) if '_addanother' in request.POST: return HttpResponseRedirect('../item_add/') elif '_save' in request.POST: return HttpResponseRedirect('../') elif '_continue' in request.POST: return response else: return HttpResponseRedirect('') def get_form(self, request, obj=None, **kwargs): """Returns modified form for TreeItem model. 'Parent' field choices are built by sitetree itself. """ if obj is not None and obj.parent is not None: self.previous_parent = obj.parent previous_parent_id = self.previous_parent.id else: previous_parent_id = None my_choice_field = TreeItemChoiceField(self.tree, initial=previous_parent_id) form = super(TreeItemAdmin, self).get_form(request, obj, **kwargs) my_choice_field.label = form.base_fields['parent'].label my_choice_field.help_text = form.base_fields['parent'].help_text # Replace 'parent' TreeItem field with new appropriate one form.base_fields['parent'] = my_choice_field # Try to resolve all currently registered url names including those in namespaces. if not getattr(self, 'known_url_names', False): self.known_url_names = [] self.known_url_rules = [] resolver = get_resolver(get_urlconf()) for ns, (url_prefix, ns_resolver) in resolver.namespace_dict.items(): if ns != 'admin': self._stack_known_urls(ns_resolver.reverse_dict, ns) self._stack_known_urls(resolver.reverse_dict) self.known_url_rules = sorted(self.known_url_rules) form.known_url_names_hint = _( 'You are seeing this warning because "URL as Pattern" option is active and pattern entered above ' 'seems to be invalid. Currently registered URL pattern names and parameters: ') form.known_url_names = self.known_url_names form.known_url_rules = self.known_url_rules return form def _stack_known_urls(self, reverse_dict, ns=None): for url_name, url_rules in reverse_dict.items(): if isinstance(url_name, six.string_types): if ns is not None: url_name = '%s:%s' % (ns, url_name) self.known_url_names.append(url_name) self.known_url_rules.append('%s %s' % (url_name, ' '.join(url_rules[0][0][1]))) def get_tree(self, request, tree_id, item_id=None): """Fetches Tree for current or given TreeItem.""" if tree_id is None: tree_id = self.get_object(request, item_id).tree_id self.tree = MODEL_TREE_CLASS._default_manager.get(pk=tree_id) self.tree.verbose_name_plural = self.tree._meta.verbose_name_plural self.tree.urls = _TREE_URLS return self.tree def item_add(self, request, tree_id): tree = self.get_tree(request, tree_id) return self.add_view(request, extra_context={'tree': tree}) def item_edit(self, request, item_id, tree_id=None): tree = self.get_tree(request, tree_id, item_id) return self.change_view(request, item_id, extra_context={'tree': tree}) def item_delete(self, request, item_id, tree_id=None): tree = self.get_tree(request, tree_id, item_id) return self.delete_view(request, item_id, extra_context={'tree': tree}) def item_history(self, request, item_id, tree_id=None): tree = self.get_tree(request, tree_id, item_id) return self.history_view(request, item_id, extra_context={'tree': tree}) def item_move(self, request, tree_id, item_id, direction): """Moves item up or down by swapping 'sort_order' field values of neighboring items.""" current_item = MODEL_TREE_ITEM_CLASS._default_manager.get(pk=item_id) if direction == 'up': sort_order = 'sort_order' else: sort_order = '-sort_order' siblings = MODEL_TREE_ITEM_CLASS._default_manager.filter( parent=current_item.parent, tree=current_item.tree ).order_by(sort_order) previous_item = None for item in siblings: if item != current_item: previous_item = item else: break if previous_item is not None: current_item_sort_order = current_item.sort_order previous_item_sort_order = previous_item.sort_order current_item.sort_order = previous_item_sort_order previous_item.sort_order = current_item_sort_order current_item.save() previous_item.save() return HttpResponseRedirect('../../') def save_model(self, request, obj, form, change): """Saves TreeItem model under certain Tree. Handles item's parent assignment exception. """ if change: # No, you're not allowed to make item parent of itself if obj.parent is not None and obj.parent.id == obj.id: obj.parent = self.previous_parent messages.warning( request, _("Item's parent left unchanged. Item couldn't be parent to itself."), '', True) obj.tree = self.tree obj.save() def redirects_handler(*args, **kwargs): """Fixes Admin contrib redirects compatibility problems introduced in Django 1.4 by url handling changes. """ referer = args[0].META['HTTP_REFERER'] shift = '../' if 'delete' in referer: # Weird enough 'delete' is not handled by TreeItemAdmin::response_change(). shift += '../' elif 'history' in referer: if 'item_id' not in kwargs: # Encountered request from history page to return to tree layout page. shift += '../' return HttpResponseRedirect(referer + shift) class TreeAdmin(admin.ModelAdmin): list_display = ('alias', 'title') list_display_links = ('title', 'alias') search_fields = ['title', 'alias'] ordering = ['title', 'alias'] actions = None change_form_template = 'admin/sitetree/tree/change_form.html' def __init__(self, *args, **kwargs): if SMUGGLER_INSTALLED: self.change_list_template = 'admin/sitetree/tree/change_list_.html' super(TreeAdmin, self).__init__(*args, **kwargs) self.tree_admin = _ITEM_ADMIN()(MODEL_TREE_ITEM_CLASS, admin.site) def render_change_form(self, request, context, **kwargs): context['icon_ext'] = '.svg' if DJANGO_POST_19 else '.gif' context['django19'] = DJANGO_POST_19 return super(TreeAdmin, self).render_change_form(request, context, **kwargs) def get_urls(self): """Manages not only TreeAdmin URLs but also TreeItemAdmin URLs.""" urls = super(TreeAdmin, self).get_urls() prefix_change = 'change/' if DJANGO_POST_19 else '' sitetree_urls = [ # Ignore urls.W002. Leading slash is in the right place. url(r'^/$', redirects_handler, name=get_tree_item_url_name('changelist')), url(r'^((?P\d+)/)?%sitem_add/$' % prefix_change, self.admin_site.admin_view(self.tree_admin.item_add), name=get_tree_item_url_name('add')), url(r'^(?P\d+)/%sitem_(?P\d+)/$' % prefix_change, self.admin_site.admin_view(self.tree_admin.item_edit), name=get_tree_item_url_name('change')), url(r'^%sitem_(?P\d+)/$' % prefix_change, self.admin_site.admin_view(self.tree_admin.item_edit), name=get_tree_item_url_name('change')), url(r'^((?P\d+)/)?%sitem_(?P\d+)/delete/$' % prefix_change, self.admin_site.admin_view(self.tree_admin.item_delete), name=get_tree_item_url_name('delete')), url(r'^((?P\d+)/)?%sitem_(?P\d+)/history/$' % prefix_change, self.admin_site.admin_view(self.tree_admin.item_history), name=get_tree_item_url_name('history')), url(r'^(?P\d+)/%sitem_(?P\d+)/move_(?P(up|down))/$' % prefix_change, self.admin_site.admin_view(self.tree_admin.item_move), name=get_tree_item_url_name('move')), ] if not DJANGO_POST_19: sitetree_urls = patterns_func('', *sitetree_urls) if SMUGGLER_INSTALLED: sitetree_urls += (url(r'^dump_all/$', self.admin_site.admin_view(self.dump_view), name='sitetree_dump'),) return sitetree_urls + urls @classmethod def dump_view(cls, request): """Dumps sitetrees with items using django-smuggler. :param request: :return: """ from smuggler.views import dump_to_response return dump_to_response(request, [MODEL_TREE, MODEL_TREE_ITEM], filename_prefix='sitetrees') _reregister_tree_admin() django-sitetree-1.5.1/sitetree/config.py000066400000000000000000000003311263651575600202620ustar00rootroot00000000000000from django.apps import AppConfig from django.utils.translation import ugettext_lazy as _ class SitetreeConfig(AppConfig): """Sitetree configuration.""" name = 'sitetree' verbose_name = _('Site Trees') django-sitetree-1.5.1/sitetree/fields.py000066400000000000000000000036241263651575600202730ustar00rootroot00000000000000from django import template from django.template.base import Parser, Token, TOKEN_BLOCK from django.forms import ChoiceField from django.utils.safestring import mark_safe from .templatetags.sitetree import sitetree_tree from .utils import get_tree_model, get_tree_item_model MODEL_TREE_CLASS = get_tree_model() MODEL_TREE_ITEM_CLASS = get_tree_item_model() class TreeItemChoiceField(ChoiceField): """Generic sitetree item field. Customized ChoiceField with TreeItems of a certain tree. Accepts the `tree` kwarg - tree model or alias. Use `initial` kwarg to set initial sitetree item by its ID. """ template = 'admin/sitetree/tree/tree_combo.html' root_title = '---------' def __init__(self, tree, required=True, widget=None, label=None, initial=None, help_text=None, *args, **kwargs): super(TreeItemChoiceField, self).__init__(required=required, widget=widget, label=label, initial=initial, help_text=help_text, *args, **kwargs) if isinstance(tree, MODEL_TREE_CLASS): tree = tree.alias self.tree = tree self.choices = self._build_choices() def _build_choices(self): """Build choices list runtime using 'sitetree_tree' tag""" tree_token = u'sitetree_tree from "%s" template "%s"' % (self.tree, self.template) choices_str = sitetree_tree( Parser(None), Token(token_type=TOKEN_BLOCK, contents=tree_token) ).render(template.Context(current_app='admin')) tree_choices = [('', self.root_title)] for line in choices_str.splitlines(): if line.strip(): splitted = line.split(':::') tree_choices.append((splitted[0], mark_safe(splitted[1]))) return tree_choices def clean(self, value): if not value: return None return MODEL_TREE_ITEM_CLASS.objects.get(pk=value) django-sitetree-1.5.1/sitetree/forms.py000066400000000000000000000013701263651575600201470ustar00rootroot00000000000000from django import forms from .fields import TreeItemChoiceField class TreeItemForm(forms.Form): """Generic sitetree form. Accepts the following kwargs: - `tree`: tree model or alias - `tree_item`: ID of an initial tree item """ choice_field_class = TreeItemChoiceField def __init__(self, *args, **kwargs): tree = kwargs.pop('tree', None) tree_item = kwargs.pop('tree_item', None) super(TreeItemForm, self).__init__(*args, **kwargs) # autocomplete off - deals with Firefox form caching # https://bugzilla.mozilla.org/show_bug.cgi?id=46845 self.fields['tree_item'] = self.choice_field_class(tree, initial=tree_item, widget=forms.Select(attrs={'autocomplete': 'off'})) django-sitetree-1.5.1/sitetree/locale/000077500000000000000000000000001263651575600177055ustar00rootroot00000000000000django-sitetree-1.5.1/sitetree/locale/de/000077500000000000000000000000001263651575600202755ustar00rootroot00000000000000django-sitetree-1.5.1/sitetree/locale/de/LC_MESSAGES/000077500000000000000000000000001263651575600220625ustar00rootroot00000000000000django-sitetree-1.5.1/sitetree/locale/de/LC_MESSAGES/django.mo000066400000000000000000000122101263651575600236550ustar00rootroot000000000000005Gl! B ON C  @@Z 5SH8 P] oy I, C: ~     /A $q ) (    &      2 @> n _g |U]Igmt y&2g*4Pa w U1-:Vh>387P%' (1.# 32/! +"-$ 54  &*,)0Access settingsAdd Site Tree itemAdditional comments on this item.Additional settingsAliasAllAnyBasic settingsBreadcrumbsCheck it to grant access to this item to authenticated users only.Check it to restrict user access to this item, using Django permissions system.DescriptionDisplay settingsExact URL or URL pattern (see "Additional settings") for this item.For logged inHiddenHintItem position among other site tree items under the same parent.Item's parent left unchanged. Item couldn't be parent to itself.Logged in onlyMenuMove downMove upParentParent site tree item.Permissions granting accessPermissions interpretationRestrict access to permissionsRights RestrictionShort name to address site tree from templates.
Note: change with care.Short name to address site tree item from a template.
Reserved aliases: "trunk", "this-children", "this-siblings" and "this-ancestor-children".Show in breadcrumb pathShow in menuShow in site treeSite TreeSite Tree ItemSite Tree ItemsSite TreesSite tree item title. Can contain template variables E.g.: {{ mytitle }}.Site tree this item belongs to.Site tree title for presentational purposes.Some additional information about this item that is used as a hint.Sort orderTitleTreeURLURL as PatternWhether the given URL should be treated as a pattern.
Note: Refer to Django "URL dispatcher" documentation (e.g. "Naming URL patterns" part).Whether to show this item in a breadcrumb path.Whether to show this item in a menu.Whether to show this item in a site tree.Whether to show this item in navigation.Project-Id-Version: django-sitetree Report-Msgid-Bugs-To: https://github.com/idlesign/django-sitetree/issues POT-Creation-Date: 2012-02-09 20:11+0700 PO-Revision-Date: 2012-05-09 21:00+0700 Last-Translator: Igor 'idle sign' Starikov Language-Team: LANGUAGE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Language: de Plural-Forms: nplurals=2; plural=(n != 1) BerechtigungenSite Baum Element hinzufügenWeitere Bemerkungen zu diesem Element.Erweiterte EinstellungenAliasAlleBeliebigeGrundeinstellungenBreadcrumbsSoll dieses Element nur angemeldeten Benutzern angezeigt werden?Soll der Zugang zu diesem Element auf Benutzer mit den unten definierten Berechtigungen eingeschränkt werden?BeschreibungDarstellungGenanaue URL oder ein URL Pattern (siehe "Erweiterte Einstellungen") für das aktuelle Element.Nur für eingeloggteVerstecktMouseover-TextPosition des Elements im Vergleich zu anderen Elementen mit dem selben Elternelement.Elternelement wurde nicht verändert. Das Element kann nicht sein eigenes Elternelement sein.Nur für angemeldete BenutzerMenüRunterHochElternelementElternelement im Site BaumBerechtigung(en) für ZugangskontrolleAnwendung der BerechtigungenZugang für bestimmte Berechtigungen einschränkenZugangskontrolleAbkürzung um Site Baum vom Template aus zu adressieren.
Hinweis: Vorsicht beim Verändern!Abkürzung um Site Baum vom Template aus zu adressieren.
Reservierte Aliasse: "trunk", "this-children", "this-siblings" und "this-ancestor-children".In Breadcrumb-Pfad anzeigenIn Menu anzeigenIn Site Baum anzeigenSite BaumSite Baum ElementSite Baum ElementeSite BäumeTitel des Site Baum Elements. Kann Template-Variablen enthalten (z.B. {{ mytitle }}).Zu welchem Site Baum dass dieses Element gehört.Titel des Site Baumes zu Darstellungszwecken.Weitere Informationen zum aktuellen Element, die als Mouseover-Text angezeigt werden.SortierreihenfolgeTitelBaumURLURL ist ein PatternSoll die angegebene URL als URL Pattern behandelt werden?
Hinweis: Siehe die Django-Dokumentation zum "URL Dispatcher" (z.B. der Abschnitt "Naming URL patterns").Soll dieses Element in einem Breadcrumb-Pfad angezeigt werden?Soll dieses Element in einem Menu angezeigt werden?Soll dieses Element in einem Site Baum angezeigt werden?Soll dieses Element in der Navigation angezeigt werden?django-sitetree-1.5.1/sitetree/locale/de/LC_MESSAGES/django.po000066400000000000000000000151171263651575600236710ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # # Translators: # , 2012. msgid "" msgstr "" "Project-Id-Version: django-sitetree\n" "Report-Msgid-Bugs-To: https://github.com/idlesign/django-sitetree/issues\n" "POT-Creation-Date: 2012-02-09 20:11+0700\n" "PO-Revision-Date: 2012-05-09 13:48+0000\n" "Last-Translator: gwrtheyrn \n" "Language-Team: LANGUAGE \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: de\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" #: admin.py:17 msgid "Basic settings" msgstr "Grundeinstellungen" #: admin.py:20 msgid "Access settings" msgstr "Berechtigungen" #: admin.py:24 msgid "Display settings" msgstr "Darstellung" #: admin.py:28 msgid "Additional settings" msgstr "Erweiterte Einstellungen" #: admin.py:159 msgid "Item's parent left unchanged. Item couldn't be parent to itself." msgstr "Elternelement wurde nicht verändert. Das Element kann nicht sein eigenes Elternelement sein." #: models.py:26 models.py:46 templates/admin/sitetree/tree/change_form.html:40 msgid "Title" msgstr "Titel" #: models.py:26 msgid "Site tree title for presentational purposes." msgstr "Titel des Site Baumes zu Darstellungszwecken." #: models.py:27 models.py:52 msgid "Alias" msgstr "Alias" #: models.py:27 msgid "" "Short name to address site tree from templates.
Note: change " "with care." msgstr "Abkürzung um Site Baum vom Template aus zu adressieren.
Hinweis: Vorsicht beim Verändern!" #: models.py:30 models.py:50 msgid "Site Tree" msgstr "Site Baum" #: models.py:31 msgid "Site Trees" msgstr "Site Bäume" #: models.py:42 msgid "Any" msgstr "Beliebige" #: models.py:43 msgid "All" msgstr "Alle" #: models.py:46 msgid "" "Site tree item title. Can contain template variables E.g.: {{ mytitle }}." msgstr "Titel des Site Baum Elements. Kann Template-Variablen enthalten (z.B. {{ mytitle }})." #: models.py:47 msgid "Hint" msgstr "Mouseover-Text" #: models.py:47 msgid "Some additional information about this item that is used as a hint." msgstr "Weitere Informationen zum aktuellen Element, die als Mouseover-Text angezeigt werden." #: models.py:48 templates/admin/sitetree/tree/change_form.html:41 msgid "URL" msgstr "URL" #: models.py:48 msgid "Exact URL or URL pattern (see \"Additional settings\") for this item." msgstr "Genanaue URL oder ein URL Pattern (siehe \"Erweiterte Einstellungen\") für das aktuelle Element." #: models.py:49 msgid "URL as Pattern" msgstr "URL ist ein Pattern" #: models.py:49 msgid "" "Whether the given URL should be treated as a pattern.
Note: " "Refer to Django \"URL dispatcher\" documentation (e.g. \"Naming URL " "patterns\" part)." msgstr "Soll die angegebene URL als URL Pattern behandelt werden?
Hinweis: Siehe die Django-Dokumentation zum \"URL Dispatcher\" (z.B. der Abschnitt \"Naming URL patterns\")." #: models.py:50 msgid "Site tree this item belongs to." msgstr "Zu welchem Site Baum dass dieses Element gehört." #: models.py:51 templates/admin/sitetree/tree/change_form.html:34 msgid "Hidden" msgstr "Versteckt" #: models.py:51 msgid "Whether to show this item in navigation." msgstr "Soll dieses Element in der Navigation angezeigt werden?" #: models.py:52 msgid "" "Short name to address site tree item from a template.
Reserved " "aliases: \"trunk\", \"this-children\", \"this-siblings\" and \"this-" "ancestor-children\"." msgstr "Abkürzung um Site Baum vom Template aus zu adressieren.
Reservierte Aliasse: \"trunk\", \"this-children\", \"this-siblings\" und \"this-ancestor-children\"." #: models.py:53 msgid "Description" msgstr "Beschreibung" #: models.py:53 msgid "Additional comments on this item." msgstr "Weitere Bemerkungen zu diesem Element." #: models.py:54 msgid "Show in menu" msgstr "In Menu anzeigen" #: models.py:54 msgid "Whether to show this item in a menu." msgstr "Soll dieses Element in einem Menu angezeigt werden?" #: models.py:55 msgid "Show in breadcrumb path" msgstr "In Breadcrumb-Pfad anzeigen" #: models.py:55 msgid "Whether to show this item in a breadcrumb path." msgstr "Soll dieses Element in einem Breadcrumb-Pfad angezeigt werden?" #: models.py:56 msgid "Show in site tree" msgstr "In Site Baum anzeigen" #: models.py:56 msgid "Whether to show this item in a site tree." msgstr "Soll dieses Element in einem Site Baum angezeigt werden?" #: models.py:57 msgid "Logged in only" msgstr "Nur für angemeldete Benutzer" #: models.py:57 msgid "Check it to grant access to this item to authenticated users only." msgstr "Soll dieses Element nur angemeldeten Benutzern angezeigt werden?" #: models.py:58 msgid "Restrict access to permissions" msgstr "Zugang für bestimmte Berechtigungen einschränken" #: models.py:58 msgid "" "Check it to restrict user access to this item, using Django permissions " "system." msgstr "Soll der Zugang zu diesem Element auf Benutzer mit den unten definierten Berechtigungen eingeschränkt werden?" #: models.py:59 msgid "Permissions granting access" msgstr "Berechtigung(en) für Zugangskontrolle" #: models.py:60 msgid "Permissions interpretation" msgstr "Anwendung der Berechtigungen" #: models.py:63 msgid "Parent" msgstr "Elternelement" #: models.py:63 msgid "Parent site tree item." msgstr "Elternelement im Site Baum" #: models.py:64 templates/admin/sitetree/tree/change_form.html:42 msgid "Sort order" msgstr "Sortierreihenfolge" #: models.py:64 msgid "Item position among other site tree items under the same parent." msgstr "Position des Elements im Vergleich zu anderen Elementen mit dem selben Elternelement." #: models.py:77 msgid "Site Tree Item" msgstr "Site Baum Element" #: models.py:78 templates/admin/sitetree/tree/change_form.html:17 msgid "Site Tree Items" msgstr "Site Baum Elemente" #: templates/admin/sitetree/tree/change_form.html:24 msgid "Add Site Tree item" msgstr "Site Baum Element hinzufügen" #: templates/admin/sitetree/tree/change_form.html:35 msgid "Menu" msgstr "Menü" #: templates/admin/sitetree/tree/change_form.html:36 msgid "Breadcrumbs" msgstr "Breadcrumbs" #: templates/admin/sitetree/tree/change_form.html:37 msgid "Tree" msgstr "Baum" #: templates/admin/sitetree/tree/change_form.html:38 msgid "Rights Restriction" msgstr "Zugangskontrolle" #: templates/admin/sitetree/tree/change_form.html:39 msgid "For logged in" msgstr "Nur für eingeloggte" #: templates/admin/sitetree/tree/tree.html:22 msgid "Move up" msgstr "Hoch" #: templates/admin/sitetree/tree/tree.html:24 msgid "Move down" msgstr "Runter" django-sitetree-1.5.1/sitetree/locale/en/000077500000000000000000000000001263651575600203075ustar00rootroot00000000000000django-sitetree-1.5.1/sitetree/locale/en/LC_MESSAGES/000077500000000000000000000000001263651575600220745ustar00rootroot00000000000000django-sitetree-1.5.1/sitetree/locale/en/LC_MESSAGES/django.po000066400000000000000000000125411263651575600237010ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2013-09-25 22:11+0700\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #: admin.py:81 msgid "Basic settings" msgstr "" #: admin.py:84 msgid "Access settings" msgstr "" #: admin.py:88 msgid "Display settings" msgstr "" #: admin.py:92 msgid "Additional settings" msgstr "" #: admin.py:153 msgid "" "You are seeing this warning because \"URL as Pattern\" option is active and " "pattern entered above seems to be invalid. Currently registered URL pattern " "names and parameters: " msgstr "" #: admin.py:230 msgid "Item's parent left unchanged. Item couldn't be parent to itself." msgstr "" #: models.py:31 models.py:56 templates/admin/sitetree/tree/change_form.html:41 msgid "Title" msgstr "" #: models.py:31 msgid "Site tree title for presentational purposes." msgstr "" #: models.py:32 models.py:62 msgid "Alias" msgstr "" #: models.py:32 msgid "" "Short name to address site tree from templates.
Note: change " "with care." msgstr "" #: models.py:36 models.py:60 msgid "Site Tree" msgstr "" #: models.py:37 msgid "Site Trees" msgstr "" #: models.py:52 msgid "Any" msgstr "" #: models.py:53 msgid "All" msgstr "" #: models.py:56 msgid "" "Site tree item title. Can contain template variables E.g.: {{ mytitle }}." msgstr "" #: models.py:57 msgid "Hint" msgstr "" #: models.py:57 msgid "Some additional information about this item that is used as a hint." msgstr "" #: models.py:58 templates/admin/sitetree/tree/change_form.html:42 msgid "URL" msgstr "" #: models.py:58 msgid "Exact URL or URL pattern (see \"Additional settings\") for this item." msgstr "" #: models.py:59 msgid "URL as Pattern" msgstr "" #: models.py:59 msgid "" "Whether the given URL should be treated as a pattern.
Note: " "Refer to Django \"URL dispatcher\" documentation (e.g. \"Naming URL patterns" "\" part)." msgstr "" #: models.py:60 msgid "Site tree this item belongs to." msgstr "" #: models.py:61 templates/admin/sitetree/tree/change_form.html:34 msgid "Hidden" msgstr "" #: models.py:61 msgid "Whether to show this item in navigation." msgstr "" #: models.py:62 #, python-format msgid "" "Short name to address site tree item from a template.
Reserved " "aliases: \"%s\"." msgstr "" #: models.py:63 msgid "Description" msgstr "" #: models.py:63 msgid "Additional comments on this item." msgstr "" #: models.py:64 msgid "Show in menu" msgstr "" #: models.py:64 msgid "Whether to show this item in a menu." msgstr "" #: models.py:65 msgid "Show in breadcrumb path" msgstr "" #: models.py:65 msgid "Whether to show this item in a breadcrumb path." msgstr "" #: models.py:66 msgid "Show in site tree" msgstr "" #: models.py:66 msgid "Whether to show this item in a site tree." msgstr "" #: models.py:67 msgid "Logged in only" msgstr "" #: models.py:67 msgid "Check it to grant access to this item to authenticated users only." msgstr "" #: models.py:68 templates/admin/sitetree/tree/change_form.html:40 msgid "Guests only" msgstr "" #: models.py:68 msgid "Check it to grant access to this item to guests only." msgstr "" #: models.py:69 msgid "Restrict access to permissions" msgstr "" #: models.py:69 msgid "" "Check it to restrict user access to this item, using Django permissions " "system." msgstr "" #: models.py:70 msgid "Permissions granting access" msgstr "" #: models.py:71 msgid "Permissions interpretation" msgstr "" #: models.py:71 msgid "" "Any — user should have any of chosen permissions. All " "— user should have all chosen permissions." msgstr "" #: models.py:74 msgid "Parent" msgstr "" #: models.py:74 msgid "Parent site tree item." msgstr "" #: models.py:75 templates/admin/sitetree/tree/change_form.html:43 msgid "Sort order" msgstr "" #: models.py:75 msgid "Item position among other site tree items under the same parent." msgstr "" #: models.py:89 msgid "Site Tree Item" msgstr "" #: models.py:90 templates/admin/sitetree/tree/change_form.html:17 msgid "Site Tree Items" msgstr "" #: templates/admin/sitetree/tree/change_form.html:24 msgid "Add Site Tree item" msgstr "" #: templates/admin/sitetree/tree/change_form.html:35 msgid "Menu" msgstr "" #: templates/admin/sitetree/tree/change_form.html:36 msgid "Breadcrumbs" msgstr "" #: templates/admin/sitetree/tree/change_form.html:37 msgid "Tree" msgstr "" #: templates/admin/sitetree/tree/change_form.html:38 msgid "Restricted" msgstr "" #: templates/admin/sitetree/tree/change_form.html:39 msgid "Users only" msgstr "" #: templates/admin/sitetree/tree/tree.html:23 msgid "Move up" msgstr "" #: templates/admin/sitetree/tree/tree.html:25 msgid "Move down" msgstr "" #: templates/admin/sitetree/treeitem/breadcrumbs.html:5 msgid "Home" msgstr "" #: templates/admin/sitetree/treeitem/breadcrumbs.html:11 msgid "Delete" msgstr "" #: templates/admin/sitetree/treeitem/breadcrumbs.html:15 msgid "History" msgstr "" #: templates/admin/sitetree/treeitem/breadcrumbs.html:17 msgid "Add" msgstr "" django-sitetree-1.5.1/sitetree/locale/es/000077500000000000000000000000001263651575600203145ustar00rootroot00000000000000django-sitetree-1.5.1/sitetree/locale/es/LC_MESSAGES/000077500000000000000000000000001263651575600221015ustar00rootroot00000000000000django-sitetree-1.5.1/sitetree/locale/es/LC_MESSAGES/django.mo000066400000000000000000000131201263651575600236750ustar00rootroot000000000000006I|! B#Of C %,@1@r .MS`P hu  I ,% CR     /Y $ ) (     + 06<C TWa[ "Q< TU b|!3lM(Y a(M$vJ /-003a 4' 0%*/6,( 21 $&5 3 )!-#.+"Access settingsAdd Site Tree itemAdditional comments on this item.Additional settingsAliasAllAnyBasic settingsBreadcrumbsCheck it to grant access to this item to authenticated users only.Check it to restrict user access to this item, using Django permissions system.DescriptionDisplay settingsExact URL or URL pattern (see "Additional settings") for this item.For logged inHiddenHintItem position among other site tree items under the same parent.Item's parent left unchanged. Item couldn't be parent to itself.Logged in onlyMenuMove downMove upParentParent site tree item.Permissions granting accessPermissions interpretationRestrict access to permissionsRights RestrictionShort name to address site tree from templates.
Note: change with care.Short name to address site tree item from a template.
Reserved aliases: "trunk", "this-children", "this-siblings" and "this-ancestor-children".Show in breadcrumb pathShow in menuShow in site treeSite TreeSite Tree ItemSite Tree ItemsSite TreesSite tree item title. Can contain template variables E.g.: {{ mytitle }}.Site tree this item belongs to.Site tree title for presentational purposes.Some additional information about this item that is used as a hint.Sort orderTitleTreeURLURL as PatternWhether the given URL should be treated as a pattern.
Note: Refer to Django "URL dispatcher" documentation (e.g. "Naming URL patterns" part).Whether to show this item in a breadcrumb path.Whether to show this item in a menu.Whether to show this item in a site tree.Whether to show this item in navigation.You are seeing this warning because "URL as Pattern" option is active and pattern entered above seems to be invalid. Currently registered URL pattern names and parameters: Project-Id-Version: django-sitetree Report-Msgid-Bugs-To: https://github.com/idlesign/django-sitetree/issues POT-Creation-Date: 2012-09-11 22:07+0700 PO-Revision-Date: 2013-08-05 23:30+0700 Last-Translator: Igor 'idle sign' Starikov Language-Team: Spanish (Spain) (http://www.transifex.com/projects/p/django-sitetree/language/es_ES/) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Language: es_ES Plural-Forms: nplurals=2; plural=(n != 1); X-Generator: Poedit 1.5.4 Ajustes de accesoAñadir Sección de Mapa WebComentarios adicionales sobre esta secciónAjustes adicionalesAliasTodosAlgunoAjustes básicosMigas de PanMarcar para permitir el acceso a esta sección exclusivamente a usuarios identificados.Marcar para restringir el acceso a esta sección mediante el sistema de permisos de Django.DescripciónAjustes de visualizaciónURL exacta o patrón de URL (diríjase a "Ajustes adicionales") de esta sección.Solo sesión iniciadaOcultoIndicaciónPosición de la sección entre las demás secciones del mapa web con el mismo padre.El padre de la sección no se modificó. La sección no puede ser padre de sí misma.Solo con sesión iniciadaMenúDesplazar hacia abajoDesplazar hacia arribaPadreSección padre en el mapa web.Permisos que conceden accesoInterpretación de los permisosRestringir acceso según permisosRestricción por permisosNombre corto para referirse al mapa web desde las plantillas.
Nota:Modifíquese con precaución.Nombre corto para referirse al mapa web desde una plantilla.
Alias reservados: "trunk", "this-childen", "this-siblings" y "this-ancestor-childen".Mostrar en las migas de pan (breadcumbs)Mostrar en el menúMostrar en el mapa webMapa WebSección de Mapa WebSecciones de Mapa WebMapas WebTítulo para la sección del mapa web. Puede contener variables de plantilla Ej.: {{ mititulo }}.Mapa web al que pertenece esta sección.Título representativo del mapa web.Información adicional sobre esta sección que se usará como indicación.Orden de apariciónTítuloMapaURLURL como PatrónSi la URL proporcionada debe de tratarse como un patrón.
Véase la documentación de Django sobre "URL dispatcher" (en inglés) (Por ej. la sección "Naming URL patterns").Mostrar o no esta sección en las migas de pan.Si se debe mostrar esta sección en el menú.Si se debe mostrar esta sección en el mapa web.Si se debe mostrar esta sección en la navegación.Estás viendo esta advertencia porque la opción "URL como Patrón" está activada y el patrón introducido no es válido. Nombres de patrones registrados actualmente y sus parámetros :django-sitetree-1.5.1/sitetree/locale/es/LC_MESSAGES/django.po000066400000000000000000000160551263651575600237120ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # # Translators: # AdrianLC , 2013 msgid "" msgstr "" "Project-Id-Version: django-sitetree\n" "Report-Msgid-Bugs-To: https://github.com/idlesign/django-sitetree/issues\n" "POT-Creation-Date: 2012-09-11 22:07+0700\n" "PO-Revision-Date: 2013-07-22 16:52+0000\n" "Last-Translator: AdrianLC \n" "Language-Team: Spanish (Spain) (http://www.transifex.com/projects/p/django-sitetree/language/es_ES/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: es_ES\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #: admin.py:54 msgid "Basic settings" msgstr "Ajustes básicos" #: admin.py:57 msgid "Access settings" msgstr "Ajustes de acceso" #: admin.py:61 msgid "Display settings" msgstr "Ajustes de visualización" #: admin.py:65 msgid "Additional settings" msgstr "Ajustes adicionales" #: admin.py:148 msgid "" "You are seeing this warning because \"URL as Pattern\" option is active and " "pattern entered above seems to be invalid. Currently registered URL pattern " "names and parameters: " msgstr "Estás viendo esta advertencia porque la opción \"URL como Patrón\" está activada y el patrón introducido no es válido. Nombres de patrones registrados actualmente y sus parámetros :" #: admin.py:211 msgid "Item's parent left unchanged. Item couldn't be parent to itself." msgstr "El padre de la sección no se modificó. La sección no puede ser padre de sí misma." #: models.py:26 models.py:46 templates/admin/sitetree/tree/change_form.html:40 msgid "Title" msgstr "Título" #: models.py:26 msgid "Site tree title for presentational purposes." msgstr "Título representativo del mapa web." #: models.py:27 models.py:52 msgid "Alias" msgstr "Alias" #: models.py:27 msgid "" "Short name to address site tree from templates.
Note: change " "with care." msgstr "Nombre corto para referirse al mapa web desde las plantillas.
Nota:Modifíquese con precaución." #: models.py:30 models.py:50 msgid "Site Tree" msgstr "Mapa Web" #: models.py:31 msgid "Site Trees" msgstr "Mapas Web" #: models.py:42 msgid "Any" msgstr "Alguno" #: models.py:43 msgid "All" msgstr "Todos" #: models.py:46 msgid "" "Site tree item title. Can contain template variables E.g.: {{ mytitle }}." msgstr "Título para la sección del mapa web. Puede contener variables de plantilla Ej.: {{ mititulo }}." #: models.py:47 msgid "Hint" msgstr "Indicación" #: models.py:47 msgid "Some additional information about this item that is used as a hint." msgstr "Información adicional sobre esta sección que se usará como indicación." #: models.py:48 templates/admin/sitetree/tree/change_form.html:41 msgid "URL" msgstr "URL" #: models.py:48 msgid "Exact URL or URL pattern (see \"Additional settings\") for this item." msgstr "URL exacta o patrón de URL (diríjase a \"Ajustes adicionales\") de esta sección." #: models.py:49 msgid "URL as Pattern" msgstr "URL como Patrón" #: models.py:49 msgid "" "Whether the given URL should be treated as a pattern.
Note: " "Refer to Django \"URL dispatcher\" documentation (e.g. \"Naming URL " "patterns\" part)." msgstr "Si la URL proporcionada debe de tratarse como un patrón.
Véase la documentación de Django sobre \"URL dispatcher\" (en inglés) (Por ej. la sección \"Naming URL patterns\")." #: models.py:50 msgid "Site tree this item belongs to." msgstr "Mapa web al que pertenece esta sección." #: models.py:51 templates/admin/sitetree/tree/change_form.html:34 msgid "Hidden" msgstr "Oculto" #: models.py:51 msgid "Whether to show this item in navigation." msgstr "Si se debe mostrar esta sección en la navegación." #: models.py:52 msgid "" "Short name to address site tree item from a template.
Reserved " "aliases: \"trunk\", \"this-children\", \"this-siblings\" and \"this-" "ancestor-children\"." msgstr "Nombre corto para referirse al mapa web desde una plantilla.
Alias reservados: \"trunk\", \"this-childen\", \"this-siblings\" y \"this-ancestor-childen\"." #: models.py:53 msgid "Description" msgstr "Descripción" #: models.py:53 msgid "Additional comments on this item." msgstr "Comentarios adicionales sobre esta sección" #: models.py:54 msgid "Show in menu" msgstr "Mostrar en el menú" #: models.py:54 msgid "Whether to show this item in a menu." msgstr "Si se debe mostrar esta sección en el menú." #: models.py:55 msgid "Show in breadcrumb path" msgstr "Mostrar en las migas de pan (breadcumbs)" #: models.py:55 msgid "Whether to show this item in a breadcrumb path." msgstr "Mostrar o no esta sección en las migas de pan." #: models.py:56 msgid "Show in site tree" msgstr "Mostrar en el mapa web" #: models.py:56 msgid "Whether to show this item in a site tree." msgstr "Si se debe mostrar esta sección en el mapa web." #: models.py:57 msgid "Logged in only" msgstr "Solo con sesión iniciada" #: models.py:57 msgid "Check it to grant access to this item to authenticated users only." msgstr "Marcar para permitir el acceso a esta sección exclusivamente a usuarios identificados." #: models.py:58 msgid "Restrict access to permissions" msgstr "Restringir acceso según permisos" #: models.py:58 msgid "" "Check it to restrict user access to this item, using Django permissions " "system." msgstr "Marcar para restringir el acceso a esta sección mediante el sistema de permisos de Django." #: models.py:59 msgid "Permissions granting access" msgstr "Permisos que conceden acceso" #: models.py:60 msgid "Permissions interpretation" msgstr "Interpretación de los permisos" #: models.py:63 msgid "Parent" msgstr "Padre" #: models.py:63 msgid "Parent site tree item." msgstr "Sección padre en el mapa web." #: models.py:64 templates/admin/sitetree/tree/change_form.html:42 msgid "Sort order" msgstr "Orden de aparición" #: models.py:64 msgid "Item position among other site tree items under the same parent." msgstr "Posición de la sección entre las demás secciones del mapa web con el mismo padre." #: models.py:77 msgid "Site Tree Item" msgstr "Sección de Mapa Web" #: models.py:78 templates/admin/sitetree/tree/change_form.html:17 msgid "Site Tree Items" msgstr "Secciones de Mapa Web" #: templates/admin/sitetree/tree/change_form.html:24 msgid "Add Site Tree item" msgstr "Añadir Sección de Mapa Web" #: templates/admin/sitetree/tree/change_form.html:35 msgid "Menu" msgstr "Menú" #: templates/admin/sitetree/tree/change_form.html:36 msgid "Breadcrumbs" msgstr "Migas de Pan" #: templates/admin/sitetree/tree/change_form.html:37 msgid "Tree" msgstr "Mapa" #: templates/admin/sitetree/tree/change_form.html:38 msgid "Rights Restriction" msgstr "Restricción por permisos" #: templates/admin/sitetree/tree/change_form.html:39 msgid "For logged in" msgstr "Solo sesión iniciada" #: templates/admin/sitetree/tree/tree.html:22 msgid "Move up" msgstr "Desplazar hacia arriba" #: templates/admin/sitetree/tree/tree.html:24 msgid "Move down" msgstr "Desplazar hacia abajo" django-sitetree-1.5.1/sitetree/locale/fa/000077500000000000000000000000001263651575600202735ustar00rootroot00000000000000django-sitetree-1.5.1/sitetree/locale/fa/LC_MESSAGES/000077500000000000000000000000001263651575600220605ustar00rootroot00000000000000django-sitetree-1.5.1/sitetree/locale/fa/LC_MESSAGES/django.mo000066400000000000000000000142571263651575600236700ustar00rootroot000000000000006I|! B#Of C %,@1@r .MS`P hu  I ,% CR     /Y $ ) (   \ x 1       ~#z,aF' +) +5>as1'@*W#, A  _[0RaYA 4' 0%*/6,( 21 $&5 3 )!-#.+"Access settingsAdd Site Tree itemAdditional comments on this item.Additional settingsAliasAllAnyBasic settingsBreadcrumbsCheck it to grant access to this item to authenticated users only.Check it to restrict user access to this item, using Django permissions system.DescriptionDisplay settingsExact URL or URL pattern (see "Additional settings") for this item.For logged inHiddenHintItem position among other site tree items under the same parent.Item's parent left unchanged. Item couldn't be parent to itself.Logged in onlyMenuMove downMove upParentParent site tree item.Permissions granting accessPermissions interpretationRestrict access to permissionsRights RestrictionShort name to address site tree from templates.
Note: change with care.Short name to address site tree item from a template.
Reserved aliases: "trunk", "this-children", "this-siblings" and "this-ancestor-children".Show in breadcrumb pathShow in menuShow in site treeSite TreeSite Tree ItemSite Tree ItemsSite TreesSite tree item title. Can contain template variables E.g.: {{ mytitle }}.Site tree this item belongs to.Site tree title for presentational purposes.Some additional information about this item that is used as a hint.Sort orderTitleTreeURLURL as PatternWhether the given URL should be treated as a pattern.
Note: Refer to Django "URL dispatcher" documentation (e.g. "Naming URL patterns" part).Whether to show this item in a breadcrumb path.Whether to show this item in a menu.Whether to show this item in a site tree.Whether to show this item in navigation.You are seeing this warning because "URL as Pattern" option is active and pattern entered above seems to be invalid. Currently registered URL pattern names and parameters: Project-Id-Version: django-sitetree Report-Msgid-Bugs-To: https://github.com/idlesign/django-sitetree/issues POT-Creation-Date: 2012-09-11 22:07+0700 PO-Revision-Date: 2013-02-01 22:06+0700 Last-Translator: Igor 'idle sign' Starikov Language-Team: LANGUAGE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Language: fa Plural-Forms: nplurals=1; plural=0; تنظیمات دسترسیاضافه کردن شاخهتوضیحات اضافی روی این آیتم.تنظیمات اضافیلقبهمههرتنظیمات پایهنقشه‌ی سایتتیک بزنید تا دسترسی این آیتم تنها به کاربرای تصدیق هویت شده محدود شود.تیک بزنید تا دسترسی کاربر را به اجازه‌ی دسترسی‌های خاص محدود کنید.توضیحاتتنظیمات نمایشآدرس دقبق یا قالب آدرس ( برای تنظیمات اضافی را ببینید)برای کاربران وارد شدهپنهانراهنماییمحل جاگیری آیتم در شاخه.ریشه‌‌ی آیتم بدون تغییر باقی مانده است. آیتم نمی‌تواند ریشه‌ی خود باشد.کاربران وارد شدهمنوایین آوردنبالا بردنریشهشاخه‌ی ریشه.اجازه‌های دسترسی مجازتفسیر اجازه‌های دسترسیمحدود کردن دسترسی‌ها به اجازه‌هامحدود کردن حقوقنامی کوتاه برای آدرس دهی درقالب‌های کد. نکته: با دقت تغییر دهید.نامی کوتاه که برای صدا زدن شاخه‌های سایت در یک قالب استفاده می‌شود. لقب‌های رزرو شده: "trunk" و "this-children" و "this-siblings" و "this-ancestor-children"نمایش در نقشه‌ی سایت.نمایش در منونمایش در شاخه‌های سایتشاخه‌های سایتآیتم شاخه‌های سایتآیتم‌های شاخه‌های سایتشاخه‌های سایتعنوان شاخه‌ی سایت. می‌تواند حاوی متغیر‌های قالب‌های کد باشد. مانند: {{ mytitle }}شاخه‌ای که این آیتم به آن متعلق است.عنوان نمایشی منو.برخی اطلاعات اضافی در مورد این آیتم که می‌تواند به عنوان راهنمایی استفاده شود.ترتیبعنوانشاخهآدرسآدرس به صورت قالباینکه با این آدرس باید به صورت قالب برخورد شود یا نه. اینکه این آیتم درنقشه‌ی سایت به نمایش درآید یا نه.اینکه این آیتم در یک منو به نمایش در آید یا نه.اینکه این آیتم در شاخه‌های سایت به نمایش در آید یا نه.اینکهاین آیتم باید درمسیریابی استفاده شود یا نه.شما این پیغام را می‌بینید چون گزینه‌ی «آدرس به عنوان قالب» فعال است و قالب ارائه شده غلط به نظر می‌رسد. قالب و متغیر‌های قالب‌های ثبت شده در حال حاظر: django-sitetree-1.5.1/sitetree/locale/fa/LC_MESSAGES/django.po000066400000000000000000000172171263651575600236720ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # # Translators: # Ali Javadi , 2013. msgid "" msgstr "" "Project-Id-Version: django-sitetree\n" "Report-Msgid-Bugs-To: https://github.com/idlesign/django-sitetree/issues\n" "POT-Creation-Date: 2012-09-11 22:07+0700\n" "PO-Revision-Date: 2013-01-19 17:49+0000\n" "Last-Translator: rohamn \n" "Language-Team: LANGUAGE \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: fa\n" "Plural-Forms: nplurals=1; plural=0;\n" #: admin.py:54 msgid "Basic settings" msgstr "تنظیمات پایه" #: admin.py:57 msgid "Access settings" msgstr "تنظیمات دسترسی" #: admin.py:61 msgid "Display settings" msgstr "تنظیمات نمایش" #: admin.py:65 msgid "Additional settings" msgstr "تنظیمات اضافی" #: admin.py:148 msgid "" "You are seeing this warning because \"URL as Pattern\" option is active and " "pattern entered above seems to be invalid. Currently registered URL pattern " "names and parameters: " msgstr "شما این پیغام را می‌بینید چون گزینه‌ی «آدرس به عنوان قالب» فعال است و قالب ارائه شده غلط به نظر می‌رسد. قالب و متغیر‌های قالب‌های ثبت شده در حال حاظر: " #: admin.py:211 msgid "Item's parent left unchanged. Item couldn't be parent to itself." msgstr "ریشه‌‌ی آیتم بدون تغییر باقی مانده است. آیتم نمی‌تواند ریشه‌ی خود باشد." #: models.py:26 models.py:46 templates/admin/sitetree/tree/change_form.html:40 msgid "Title" msgstr "عنوان" #: models.py:26 msgid "Site tree title for presentational purposes." msgstr "عنوان نمایشی منو." #: models.py:27 models.py:52 msgid "Alias" msgstr "لقب" #: models.py:27 msgid "" "Short name to address site tree from templates.
Note: change " "with care." msgstr "نامی کوتاه برای آدرس دهی درقالب‌های کد. نکته: با دقت تغییر دهید." #: models.py:30 models.py:50 msgid "Site Tree" msgstr "شاخه‌های سایت" #: models.py:31 msgid "Site Trees" msgstr "شاخه‌های سایت" #: models.py:42 msgid "Any" msgstr "هر" #: models.py:43 msgid "All" msgstr "همه" #: models.py:46 msgid "" "Site tree item title. Can contain template variables E.g.: {{ mytitle }}." msgstr "عنوان شاخه‌ی سایت. می‌تواند حاوی متغیر‌های قالب‌های کد باشد. مانند: {{ mytitle }}" #: models.py:47 msgid "Hint" msgstr "راهنمایی" #: models.py:47 msgid "Some additional information about this item that is used as a hint." msgstr "برخی اطلاعات اضافی در مورد این آیتم که می‌تواند به عنوان راهنمایی استفاده شود." #: models.py:48 templates/admin/sitetree/tree/change_form.html:41 msgid "URL" msgstr "آدرس" #: models.py:48 msgid "Exact URL or URL pattern (see \"Additional settings\") for this item." msgstr "آدرس دقبق یا قالب آدرس ( برای تنظیمات اضافی را ببینید)" #: models.py:49 msgid "URL as Pattern" msgstr "آدرس به صورت قالب" #: models.py:49 msgid "" "Whether the given URL should be treated as a pattern.
Note: " "Refer to Django \"URL dispatcher\" documentation (e.g. \"Naming URL " "patterns\" part)." msgstr "اینکه با این آدرس باید به صورت قالب برخورد شود یا نه. " #: models.py:50 msgid "Site tree this item belongs to." msgstr "شاخه‌ای که این آیتم به آن متعلق است." #: models.py:51 templates/admin/sitetree/tree/change_form.html:34 msgid "Hidden" msgstr "پنهان" #: models.py:51 msgid "Whether to show this item in navigation." msgstr "اینکهاین آیتم باید درمسیریابی استفاده شود یا نه." #: models.py:52 msgid "" "Short name to address site tree item from a template.
Reserved " "aliases: \"trunk\", \"this-children\", \"this-siblings\" and \"this-" "ancestor-children\"." msgstr "نامی کوتاه که برای صدا زدن شاخه‌های سایت در یک قالب استفاده می‌شود. لقب‌های رزرو شده: \"trunk\" و \"this-children\" و \"this-siblings\" و \"this-ancestor-children\"" #: models.py:53 msgid "Description" msgstr "توضیحات" #: models.py:53 msgid "Additional comments on this item." msgstr "توضیحات اضافی روی این آیتم." #: models.py:54 msgid "Show in menu" msgstr "نمایش در منو" #: models.py:54 msgid "Whether to show this item in a menu." msgstr "اینکه این آیتم در یک منو به نمایش در آید یا نه." #: models.py:55 msgid "Show in breadcrumb path" msgstr "نمایش در نقشه‌ی سایت." #: models.py:55 msgid "Whether to show this item in a breadcrumb path." msgstr "اینکه این آیتم درنقشه‌ی سایت به نمایش درآید یا نه." #: models.py:56 msgid "Show in site tree" msgstr "نمایش در شاخه‌های سایت" #: models.py:56 msgid "Whether to show this item in a site tree." msgstr "اینکه این آیتم در شاخه‌های سایت به نمایش در آید یا نه." #: models.py:57 msgid "Logged in only" msgstr "کاربران وارد شده" #: models.py:57 msgid "Check it to grant access to this item to authenticated users only." msgstr "تیک بزنید تا دسترسی این آیتم تنها به کاربرای تصدیق هویت شده محدود شود." #: models.py:58 msgid "Restrict access to permissions" msgstr "محدود کردن دسترسی‌ها به اجازه‌ها" #: models.py:58 msgid "" "Check it to restrict user access to this item, using Django permissions " "system." msgstr "تیک بزنید تا دسترسی کاربر را به اجازه‌ی دسترسی‌های خاص محدود کنید." #: models.py:59 msgid "Permissions granting access" msgstr "اجازه‌های دسترسی مجاز" #: models.py:60 msgid "Permissions interpretation" msgstr "تفسیر اجازه‌های دسترسی" #: models.py:63 msgid "Parent" msgstr "ریشه" #: models.py:63 msgid "Parent site tree item." msgstr "شاخه‌ی ریشه." #: models.py:64 templates/admin/sitetree/tree/change_form.html:42 msgid "Sort order" msgstr "ترتیب" #: models.py:64 msgid "Item position among other site tree items under the same parent." msgstr "محل جاگیری آیتم در شاخه." #: models.py:77 msgid "Site Tree Item" msgstr "آیتم شاخه‌های سایت" #: models.py:78 templates/admin/sitetree/tree/change_form.html:17 msgid "Site Tree Items" msgstr "آیتم‌های شاخه‌های سایت" #: templates/admin/sitetree/tree/change_form.html:24 msgid "Add Site Tree item" msgstr "اضافه کردن شاخه" #: templates/admin/sitetree/tree/change_form.html:35 msgid "Menu" msgstr "منو" #: templates/admin/sitetree/tree/change_form.html:36 msgid "Breadcrumbs" msgstr "نقشه‌ی سایت" #: templates/admin/sitetree/tree/change_form.html:37 msgid "Tree" msgstr "شاخه" #: templates/admin/sitetree/tree/change_form.html:38 msgid "Rights Restriction" msgstr "محدود کردن حقوق" #: templates/admin/sitetree/tree/change_form.html:39 msgid "For logged in" msgstr "برای کاربران وارد شده" #: templates/admin/sitetree/tree/tree.html:22 msgid "Move up" msgstr "بالا بردن" #: templates/admin/sitetree/tree/tree.html:24 msgid "Move down" msgstr "ایین آوردن" django-sitetree-1.5.1/sitetree/locale/nb/000077500000000000000000000000001263651575600203045ustar00rootroot00000000000000django-sitetree-1.5.1/sitetree/locale/nb/LC_MESSAGES/000077500000000000000000000000001263651575600220715ustar00rootroot00000000000000django-sitetree-1.5.1/sitetree/locale/nb/LC_MESSAGES/django.mo000066400000000000000000000132561263651575600236770ustar00rootroot00000000000000=S8z9! .B:5}O C' kw~@@! &08?Vr SY e }    I  ,: Cg     /y $ ) ( !  /EN(`:- a: W (/ 4>@CT    'C[ {SU/ DOcj s~L'C Ycjnr 7+-c;3(), 21 !3-  <#0%"'6+ $*459:=. ;7/&8Any — user should have any of chosen permissions. All — user should have all chosen permissions.Access settingsAddAdd Site Tree itemAdditional comments on this item.Additional settingsAliasAllAnyBasic settingsBreadcrumbsCheck it to grant access to this item to authenticated users only.Check it to grant access to this item to guests only.Check it to restrict user access to this item, using Django permissions system.DeleteDescriptionDisplay settingsExact URL or URL pattern (see "Additional settings") for this item.Guests onlyHiddenHintHistoryHomeItem position among other site tree items under the same parent.Item's parent left unchanged. Item couldn't be parent to itself.Logged in onlyMenuMove downMove upParentParent site tree item.Permissions granting accessPermissions interpretationRestrict access to permissionsRestrictedShort name to address site tree from templates.
Note: change with care.Short name to address site tree item from a template.
Reserved aliases: "%s".Show in breadcrumb pathShow in menuShow in site treeSite TreeSite Tree ItemSite Tree ItemsSite TreesSite tree item title. Can contain template variables E.g.: {{ mytitle }}.Site tree this item belongs to.Site tree title for presentational purposes.Some additional information about this item that is used as a hint.Sort orderTitleTreeURLURL as PatternUsers onlyWhether the given URL should be treated as a pattern.
Note: Refer to Django "URL dispatcher" documentation (e.g. "Naming URL patterns" part).Whether to show this item in a breadcrumb path.Whether to show this item in a menu.Whether to show this item in a site tree.Whether to show this item in navigation.You are seeing this warning because "URL as Pattern" option is active and pattern entered above seems to be invalid. Currently registered URL pattern names and parameters: Project-Id-Version: django-sitetree Report-Msgid-Bugs-To: POT-Creation-Date: 2013-09-25 22:11+0700 PO-Revision-Date: 2015-07-27 20:47+0600 Last-Translator: Igor 'idle sign' Starikov Language-Team: Norwegian Bokmål (http://www.transifex.com/projects/p/django-sitetree/language/nb/) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Language: nb Plural-Forms: nplurals=2; plural=(n != 1); X-Generator: Poedit 1.7.5 Noen — brukeren må ha en eller flere valgte rettigheter. Alle — brukeren må ha alle valgte rettigheter.TilgangsinnstillingerLegg tilLegg til stipunktTilleggskommentarer for dette stipunktetTilleggsinnstillingerAliasAlleNoenGrunninnstillingerNavigasjonsstiAngir at stipunktet kun er synlig for autentiserte brukereAngir at stipunktet kun er synlig for gjesterAngir at brukers tilgang til stipunktet er avhengig av rettigheter etter Djangos rettighetssystemSlettBeskrivelseVisningsinnstillingerEksakt nettadresse eller URL-mønster (se "Tilleggsinnstillinger") for dette stipunktetKun gjesterSkjultHintHistorikkHjemStipunktets posisjon blant andre stipunkter under samme forelderStipunktets forelder er uforandret. Stipunktet kan ikke være forelder til seg selv.Kun innloggetMenyFlytt nedFlytt oppForelderForelder til dette stipunktetRettigheter som gir tilgangTolkning av rettigheterBegrens tilgang med tillatelserAvgrensetKort navn for å referere til stitreet fra maler.
Merk: Endre med omhu.Kort navn for å referere til stipunkt fra maler.
Reserverte alias: "%s".Vis i navigasjonsstiVis i menyVis i nettstedskartStitreStipunktStipunkterStitrærVisningstittel for stipunkt. Kan inneholde malvariabler, f.eks. {{ title }}.Stipunkt dette punktet hører inn underStitreets visningstittelTilleggsinformasjon for dette stipunktet, gjerne brukt som et hint.SorteringTittelTreURLURL som mønsterKun brukereAngir om gitt URL skal håndteres som et mønster.
Merk: Referer til Djangos dokumentasjon for "URL dispatcher" (f.eks. "Naming URL patterns").Angir om dette stipunktet skal vises i navigasjonsstierAngir om dette stipunktet skal vises i menyerAngir om dette stipunktet skal vises i kart over nettstedetAngir om dette stipunktet skal skjules i navigasjonDu ser denne advarselen fordi "URL som mønster" er aktivert, og mønsteret over ser ut til å være ugyldig. Registrerte navn og parametre for URL-mønstre er som følger:django-sitetree-1.5.1/sitetree/locale/nb/LC_MESSAGES/django.po000066400000000000000000000167151263651575600237050ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # # Translators: # Eirik Krogstad , 2015 msgid "" msgstr "" "Project-Id-Version: django-sitetree\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2013-09-25 22:11+0700\n" "PO-Revision-Date: 2015-04-27 22:49+0000\n" "Last-Translator: Eirik Krogstad \n" "Language-Team: Norwegian Bokmål (http://www.transifex.com/projects/p/django-sitetree/language/nb/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: nb\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #: admin.py:81 msgid "Basic settings" msgstr "Grunninnstillinger" #: admin.py:84 msgid "Access settings" msgstr "Tilgangsinnstillinger" #: admin.py:88 msgid "Display settings" msgstr "Visningsinnstillinger" #: admin.py:92 msgid "Additional settings" msgstr "Tilleggsinnstillinger" #: admin.py:153 msgid "" "You are seeing this warning because \"URL as Pattern\" option is active and " "pattern entered above seems to be invalid. Currently registered URL pattern " "names and parameters: " msgstr "Du ser denne advarselen fordi \"URL som mønster\" er aktivert, og mønsteret over ser ut til å være ugyldig. Registrerte navn og parametre for URL-mønstre er som følger:" #: admin.py:230 msgid "Item's parent left unchanged. Item couldn't be parent to itself." msgstr "Menypunktets forelder er uforandret. Menypunkt kan ikke være forelder til seg selv." #: models.py:31 models.py:56 templates/admin/sitetree/tree/change_form.html:41 msgid "Title" msgstr "Tittel" #: models.py:31 msgid "Site tree title for presentational purposes." msgstr "Menytreets visningstittel" #: models.py:32 models.py:62 msgid "Alias" msgstr "Alias" #: models.py:32 msgid "" "Short name to address site tree from templates.
Note: change " "with care." msgstr "Kort navn for å referere til menytreet fra maler.
Merk: Endre med omhu." #: models.py:36 models.py:60 msgid "Site Tree" msgstr "Menytre" #: models.py:37 msgid "Site Trees" msgstr "Menytrær" #: models.py:52 msgid "Any" msgstr "Noen" #: models.py:53 msgid "All" msgstr "Alle" #: models.py:56 msgid "" "Site tree item title. Can contain template variables E.g.: {{ mytitle }}." msgstr "Visningstittel for menypunkt. Kan inneholde malvariabler, f.eks. {{ title }}." #: models.py:57 msgid "Hint" msgstr "Hint" #: models.py:57 msgid "Some additional information about this item that is used as a hint." msgstr "Tilleggsinformasjon for dette menypunktet, gjerne brukt som et hint." #: models.py:58 templates/admin/sitetree/tree/change_form.html:42 msgid "URL" msgstr "URL" #: models.py:58 msgid "Exact URL or URL pattern (see \"Additional settings\") for this item." msgstr "Eksakt nettadresse eller URL-mønster (se \"Tilleggsinnstillinger\") for dette menypunktet" #: models.py:59 msgid "URL as Pattern" msgstr "URL som mønster" #: models.py:59 msgid "" "Whether the given URL should be treated as a pattern.
Note: " "Refer to Django \"URL dispatcher\" documentation (e.g. \"Naming URL " "patterns\" part)." msgstr "Angir om gitt URL skal håndteres som et mønster.
Merk: Referer til Djangos dokumentasjon for \"URL dispatcher\" (f.eks. \"Naming URL patterns\")." #: models.py:60 msgid "Site tree this item belongs to." msgstr "Menypunkt dette punktet hører inn under" #: models.py:61 templates/admin/sitetree/tree/change_form.html:34 msgid "Hidden" msgstr "Skjult" #: models.py:61 msgid "Whether to show this item in navigation." msgstr "Angir om dette menypunktet skal skjules i navigasjon" #: models.py:62 #, python-format msgid "" "Short name to address site tree item from a template.
Reserved " "aliases: \"%s\"." msgstr "Kort navn for å referere til menypunkt fra maler.
Reserverte alias: \"%s\"." #: models.py:63 msgid "Description" msgstr "Beskrivelse" #: models.py:63 msgid "Additional comments on this item." msgstr "Tilleggskommentarer for dette menypunktet" #: models.py:64 msgid "Show in menu" msgstr "Vis i meny" #: models.py:64 msgid "Whether to show this item in a menu." msgstr "Angir om dette menypunktet skal vises i menyer" #: models.py:65 msgid "Show in breadcrumb path" msgstr "Vis i navigasjonssti" #: models.py:65 msgid "Whether to show this item in a breadcrumb path." msgstr "Angir om dette menypunktet skal vises i navigasjonsstier" #: models.py:66 msgid "Show in site tree" msgstr "Vis i nettstedskart" #: models.py:66 msgid "Whether to show this item in a site tree." msgstr "Angir om dette menypunktet skal vises i kart over nettstedet" #: models.py:67 msgid "Logged in only" msgstr "Kun innlogget" #: models.py:67 msgid "Check it to grant access to this item to authenticated users only." msgstr "Angir at menypunktet kun er synlig for autentiserte brukere" #: models.py:68 templates/admin/sitetree/tree/change_form.html:40 msgid "Guests only" msgstr "Kun gjester" #: models.py:68 msgid "Check it to grant access to this item to guests only." msgstr "Angir at menypunktet kun er synlig for gjester" #: models.py:69 msgid "Restrict access to permissions" msgstr "Begrens tilgang med tillatelser" #: models.py:69 msgid "" "Check it to restrict user access to this item, using Django permissions " "system." msgstr "Angir at brukers tilgang til menypunktet er avhengig av rettigheter etter Djangos rettighetssystem" #: models.py:70 msgid "Permissions granting access" msgstr "Rettigheter som gir tilgang" #: models.py:71 msgid "Permissions interpretation" msgstr "Tolkning av rettigheter" #: models.py:71 msgid "" "Any — user should have any of chosen permissions. All " "— user should have all chosen permissions." msgstr "Noen — brukeren må ha en eller flere valgte rettigheter. Alle — brukeren må ha alle valgte rettigheter." #: models.py:74 msgid "Parent" msgstr "Forelder" #: models.py:74 msgid "Parent site tree item." msgstr "Forelder til dette menypunktet" #: models.py:75 templates/admin/sitetree/tree/change_form.html:43 msgid "Sort order" msgstr "Sortering" #: models.py:75 msgid "Item position among other site tree items under the same parent." msgstr "Menypunktets posisjon blant andre menypunkter under samme forelder" #: models.py:89 msgid "Site Tree Item" msgstr "Menypunkt" #: models.py:90 templates/admin/sitetree/tree/change_form.html:17 msgid "Site Tree Items" msgstr "Menypunkter" #: templates/admin/sitetree/tree/change_form.html:24 msgid "Add Site Tree item" msgstr "Legg til menypunkt" #: templates/admin/sitetree/tree/change_form.html:35 msgid "Menu" msgstr "Meny" #: templates/admin/sitetree/tree/change_form.html:36 msgid "Breadcrumbs" msgstr "Navigasjonssti" #: templates/admin/sitetree/tree/change_form.html:37 msgid "Tree" msgstr "Tre" #: templates/admin/sitetree/tree/change_form.html:38 msgid "Restricted" msgstr "Avgrenset" #: templates/admin/sitetree/tree/change_form.html:39 msgid "Users only" msgstr "Kun brukere" #: templates/admin/sitetree/tree/tree.html:23 msgid "Move up" msgstr "Flytt opp" #: templates/admin/sitetree/tree/tree.html:25 msgid "Move down" msgstr "Flytt ned" #: templates/admin/sitetree/treeitem/breadcrumbs.html:5 msgid "Home" msgstr "Hjem" #: templates/admin/sitetree/treeitem/breadcrumbs.html:11 msgid "Delete" msgstr "Slett" #: templates/admin/sitetree/treeitem/breadcrumbs.html:15 msgid "History" msgstr "Historikk" #: templates/admin/sitetree/treeitem/breadcrumbs.html:17 msgid "Add" msgstr "Legg til"django-sitetree-1.5.1/sitetree/locale/ru/000077500000000000000000000000001263651575600203335ustar00rootroot00000000000000django-sitetree-1.5.1/sitetree/locale/ru/LC_MESSAGES/000077500000000000000000000000001263651575600221205ustar00rootroot00000000000000django-sitetree-1.5.1/sitetree/locale/ru/LC_MESSAGES/django.mo000066400000000000000000000177461263651575600237360ustar00rootroot00000000000000=S8z9! .B:5}O C' kw~@@! &08?Vr SY e }    I  ,: Cg     /y $ ) ( ! N !:*KHv/ #8G Xg)x> Vat j4 318e#0An -$&:ayQQR#Hl %/rQo^k Y(), 21 !3-  <#0%"'6+ $*459:=. ;7/&8Any — user should have any of chosen permissions. All — user should have all chosen permissions.Access settingsAddAdd Site Tree itemAdditional comments on this item.Additional settingsAliasAllAnyBasic settingsBreadcrumbsCheck it to grant access to this item to authenticated users only.Check it to grant access to this item to guests only.Check it to restrict user access to this item, using Django permissions system.DeleteDescriptionDisplay settingsExact URL or URL pattern (see "Additional settings") for this item.Guests onlyHiddenHintHistoryHomeItem position among other site tree items under the same parent.Item's parent left unchanged. Item couldn't be parent to itself.Logged in onlyMenuMove downMove upParentParent site tree item.Permissions granting accessPermissions interpretationRestrict access to permissionsRestrictedShort name to address site tree from templates.
Note: change with care.Short name to address site tree item from a template.
Reserved aliases: "%s".Show in breadcrumb pathShow in menuShow in site treeSite TreeSite Tree ItemSite Tree ItemsSite TreesSite tree item title. Can contain template variables E.g.: {{ mytitle }}.Site tree this item belongs to.Site tree title for presentational purposes.Some additional information about this item that is used as a hint.Sort orderTitleTreeURLURL as PatternUsers onlyWhether the given URL should be treated as a pattern.
Note: Refer to Django "URL dispatcher" documentation (e.g. "Naming URL patterns" part).Whether to show this item in a breadcrumb path.Whether to show this item in a menu.Whether to show this item in a site tree.Whether to show this item in navigation.You are seeing this warning because "URL as Pattern" option is active and pattern entered above seems to be invalid. Currently registered URL pattern names and parameters: Project-Id-Version: django-sitetree Report-Msgid-Bugs-To: https://github.com/idlesign/django-sitetree/issues POT-Creation-Date: 2013-09-25 22:11+0700 PO-Revision-Date: 2013-09-25 22:25+0700 Last-Translator: Igor 'idle sign' Starikov Language-Team: Russian (http://www.transifex.com/projects/p/django-sitetree/language/ru/) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Language: ru Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2); X-Generator: Poedit 1.5.4 Любое — пользователь должен обладать любым из выбранных прав. Все — пользователь должен обладать всеми выбранными правами.Настройки доступаДобавитьДобавить элемент древаДополнительная информация об элементе.Дополнительные настройкиПсевдонимВсеЛюбоеОсновные настройкиЦепочкаПоставьте галочку, чтобы разрешить доступ к этому элементу дерева только авторизовавшимся пользователям.Поставьте галочку, чтобы разрешить доступ к этому элементу дерева только гостям.Поставьте галочку, чтобы включить ограничение доступа к этому элементу, используя систему прав Django.УдалитьОписаниеНастройки отображенияТочный URL (ссылка) или URL шаблон (см. «Дополнительные настройки») для данного элемента.Только гостиСкрытПодсказкаИсторияДомойПозиция элемента среди других элементов того же родителя.Родитель элемента оставлен без изменений. Элемент не может являться родителем сам для себя.Только для авторизовавшихсяМенюПередвинуть нижеПередвинуть вышеРодительРодительский элемент древа.Права, необходимые для доступаИнтерпретация правОграничить доступ правамиДоступ ограниченКороткое имя для обращения к древу сайта из шаблонов.
Примечание: изменяя, будьте осторожны.Короткое имя для обращения к элементу из шаблона.
Зарезервированные псевдонимы: "%s".Отображать в навигационной цепочкеОтображать в менюОтображать в древе сайтаДрево сайтаЭлемент древа сайтаЭлементы древа сайтаДерева сайтаЗаголовок элемента древа сайта. Может содержать переменные, н.п.: {{ mytitle }}.Древо сайта, к которому принадлежит элемент.Название древа для удобства ориентирования.Дополнительная информация об элементе, которая будет использована в качестве подсказки.Порядок сортировкиЗаголовокДревоURLURL как шаблонТолько пользователиСледует ли трактовать указанный URL как шаблон.
К сведению: Обратитесь к руководству по Django, раздел «URL dispatcher» (н.п. к «Naming URL patterns») за дополнительной информацией.Следует ли показывать данный элемент в навигационной цепочке.Следует ли показывать данный элемент в меню.Следует ли показывать данный элемент в древе сайта.Следует ли скрыть данный элемент для всех типов навигации.Вы видите это предупреждение, потому что включена опция «URL как шаблон», а шаблон указанный выше, похоже, содержит ошибку. Зарегистрированные на данный момент имена шаблонов и их параметры:django-sitetree-1.5.1/sitetree/locale/ru/LC_MESSAGES/django.po000066400000000000000000000233551263651575600237320ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # # Translators: # Igor Starikov , 2011-2013 msgid "" msgstr "" "Project-Id-Version: django-sitetree\n" "Report-Msgid-Bugs-To: https://github.com/idlesign/django-sitetree/issues\n" "POT-Creation-Date: 2013-09-25 22:11+0700\n" "PO-Revision-Date: 2013-09-25 15:23+0000\n" "Last-Translator: Igor Starikov \n" "Language-Team: Russian (http://www.transifex.com/projects/p/django-sitetree/language/ru/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: ru\n" "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" #: admin.py:81 msgid "Basic settings" msgstr "Основные настройки" #: admin.py:84 msgid "Access settings" msgstr "Настройки доступа" #: admin.py:88 msgid "Display settings" msgstr "Настройки отображения" #: admin.py:92 msgid "Additional settings" msgstr "Дополнительные настройки" #: admin.py:153 msgid "" "You are seeing this warning because \"URL as Pattern\" option is active and " "pattern entered above seems to be invalid. Currently registered URL pattern " "names and parameters: " msgstr "Вы видите это предупреждение, потому что включена опция «URL как шаблон», а шаблон указанный выше, похоже, содержит ошибку. Зарегистрированные на данный момент имена шаблонов и их параметры:" #: admin.py:230 msgid "Item's parent left unchanged. Item couldn't be parent to itself." msgstr "Родитель элемента оставлен без изменений. Элемент не может являться родителем сам для себя." #: models.py:31 models.py:56 templates/admin/sitetree/tree/change_form.html:41 msgid "Title" msgstr "Заголовок" #: models.py:31 msgid "Site tree title for presentational purposes." msgstr "Название древа для удобства ориентирования." #: models.py:32 models.py:62 msgid "Alias" msgstr "Псевдоним" #: models.py:32 msgid "" "Short name to address site tree from templates.
Note: change " "with care." msgstr "Короткое имя для обращения к древу сайта из шаблонов.
Примечание: изменяя, будьте осторожны." #: models.py:36 models.py:60 msgid "Site Tree" msgstr "Древо сайта" #: models.py:37 msgid "Site Trees" msgstr "Дерева сайта" #: models.py:52 msgid "Any" msgstr "Любое" #: models.py:53 msgid "All" msgstr "Все" #: models.py:56 msgid "" "Site tree item title. Can contain template variables E.g.: {{ mytitle }}." msgstr "Заголовок элемента древа сайта. Может содержать переменные, н.п.: {{ mytitle }}." #: models.py:57 msgid "Hint" msgstr "Подсказка" #: models.py:57 msgid "Some additional information about this item that is used as a hint." msgstr "Дополнительная информация об элементе, которая будет использована в качестве подсказки." #: models.py:58 templates/admin/sitetree/tree/change_form.html:42 msgid "URL" msgstr "URL" #: models.py:58 msgid "Exact URL or URL pattern (see \"Additional settings\") for this item." msgstr "Точный URL (ссылка) или URL шаблон (см. «Дополнительные настройки») для данного элемента." #: models.py:59 msgid "URL as Pattern" msgstr "URL как шаблон" #: models.py:59 msgid "" "Whether the given URL should be treated as a pattern.
Note: " "Refer to Django \"URL dispatcher\" documentation (e.g. \"Naming URL " "patterns\" part)." msgstr "Следует ли трактовать указанный URL как шаблон.
К сведению: Обратитесь к руководству по Django, раздел «URL dispatcher» (н.п. к «Naming URL patterns») за дополнительной информацией." #: models.py:60 msgid "Site tree this item belongs to." msgstr "Древо сайта, к которому принадлежит элемент." #: models.py:61 templates/admin/sitetree/tree/change_form.html:34 msgid "Hidden" msgstr "Скрыт" #: models.py:61 msgid "Whether to show this item in navigation." msgstr "Следует ли скрыть данный элемент для всех типов навигации." #: models.py:62 #, python-format msgid "" "Short name to address site tree item from a template.
Reserved " "aliases: \"%s\"." msgstr "Короткое имя для обращения к элементу из шаблона.
Зарезервированные псевдонимы: \"%s\"." #: models.py:63 msgid "Description" msgstr "Описание" #: models.py:63 msgid "Additional comments on this item." msgstr "Дополнительная информация об элементе." #: models.py:64 msgid "Show in menu" msgstr "Отображать в меню" #: models.py:64 msgid "Whether to show this item in a menu." msgstr "Следует ли показывать данный элемент в меню." #: models.py:65 msgid "Show in breadcrumb path" msgstr "Отображать в навигационной цепочке" #: models.py:65 msgid "Whether to show this item in a breadcrumb path." msgstr "Следует ли показывать данный элемент в навигационной цепочке." #: models.py:66 msgid "Show in site tree" msgstr "Отображать в древе сайта" #: models.py:66 msgid "Whether to show this item in a site tree." msgstr "Следует ли показывать данный элемент в древе сайта." #: models.py:67 msgid "Logged in only" msgstr "Только для авторизовавшихся" #: models.py:67 msgid "Check it to grant access to this item to authenticated users only." msgstr "Поставьте галочку, чтобы разрешить доступ к этому элементу дерева только авторизовавшимся пользователям." #: models.py:68 templates/admin/sitetree/tree/change_form.html:40 msgid "Guests only" msgstr "Только гости" #: models.py:68 msgid "Check it to grant access to this item to guests only." msgstr "Поставьте галочку, чтобы разрешить доступ к этому элементу дерева только гостям." #: models.py:69 msgid "Restrict access to permissions" msgstr "Ограничить доступ правами" #: models.py:69 msgid "" "Check it to restrict user access to this item, using Django permissions " "system." msgstr "Поставьте галочку, чтобы включить ограничение доступа к этому элементу, используя систему прав Django." #: models.py:70 msgid "Permissions granting access" msgstr "Права, необходимые для доступа" #: models.py:71 msgid "Permissions interpretation" msgstr "Интерпретация прав" #: models.py:71 msgid "" "Any — user should have any of chosen permissions. All " "— user should have all chosen permissions." msgstr "Любое — пользователь должен обладать любым из выбранных прав. Все — пользователь должен обладать всеми выбранными правами." #: models.py:74 msgid "Parent" msgstr "Родитель" #: models.py:74 msgid "Parent site tree item." msgstr "Родительский элемент древа." #: models.py:75 templates/admin/sitetree/tree/change_form.html:43 msgid "Sort order" msgstr "Порядок сортировки" #: models.py:75 msgid "Item position among other site tree items under the same parent." msgstr "Позиция элемента среди других элементов того же родителя." #: models.py:89 msgid "Site Tree Item" msgstr "Элемент древа сайта" #: models.py:90 templates/admin/sitetree/tree/change_form.html:17 msgid "Site Tree Items" msgstr "Элементы древа сайта" #: templates/admin/sitetree/tree/change_form.html:24 msgid "Add Site Tree item" msgstr "Добавить элемент древа" #: templates/admin/sitetree/tree/change_form.html:35 msgid "Menu" msgstr "Меню" #: templates/admin/sitetree/tree/change_form.html:36 msgid "Breadcrumbs" msgstr "Цепочка" #: templates/admin/sitetree/tree/change_form.html:37 msgid "Tree" msgstr "Древо" #: templates/admin/sitetree/tree/change_form.html:38 msgid "Restricted" msgstr "Доступ ограничен" #: templates/admin/sitetree/tree/change_form.html:39 msgid "Users only" msgstr "Только пользователи" #: templates/admin/sitetree/tree/tree.html:23 msgid "Move up" msgstr "Передвинуть выше" #: templates/admin/sitetree/tree/tree.html:25 msgid "Move down" msgstr "Передвинуть ниже" #: templates/admin/sitetree/treeitem/breadcrumbs.html:5 msgid "Home" msgstr "Домой" #: templates/admin/sitetree/treeitem/breadcrumbs.html:11 msgid "Delete" msgstr "Удалить" #: templates/admin/sitetree/treeitem/breadcrumbs.html:15 msgid "History" msgstr "История" #: templates/admin/sitetree/treeitem/breadcrumbs.html:17 msgid "Add" msgstr "Добавить" django-sitetree-1.5.1/sitetree/locale/uk/000077500000000000000000000000001263651575600203245ustar00rootroot00000000000000django-sitetree-1.5.1/sitetree/locale/uk/LC_MESSAGES/000077500000000000000000000000001263651575600221115ustar00rootroot00000000000000django-sitetree-1.5.1/sitetree/locale/uk/LC_MESSAGES/django.mo000066400000000000000000000120751263651575600237150ustar00rootroot00000000000000)d;!  C dk@p@  0'X  * :IEC   /$) (4]3W I +  ' )< f %o  / {@ Q Z !z ! B ceC4 x/&(1II~(# _ <kKH($!"# ' &%  ) Add Site Tree itemAdditional comments on this item.Additional settingsAliasBasic settingsBreadcrumbsDescriptionDisplay settingsExact URL or URL pattern (see "Additional settings") for this item.HiddenHintItem position among other site tree items under the same parent.Item's parent left unchanged. Item couldn't be parent to itself.MenuMove downMove upParentParent site tree item.Short name to address site tree from a template.Short name to address site tree item from a template.
Reserved aliases: "trunk", "this-children" and "this-siblings".Show in breadcrumb pathShow in menuShow in site treeSite TreeSite Tree ItemSite Tree ItemsSite TreesSite tree item title. Can contain template variables E.g.: {{ mytitle }}.Site tree this item belongs to.Some additional information about this item that is used as a hint.Sort orderTitleTreeURLURL as PatternWhether the given URL should be treated as a pattern.
Note: Refer to Django "URL dispatcher" documentation (e.g. "Naming URL patterns" part).Whether to show this item in a breadcrumb path.Whether to show this item in a menu.Whether to show this item in a site tree.Whether to show this item in navigation.Project-Id-Version: django-sitetree Report-Msgid-Bugs-To: https://github.com/idlesign/django-sitetree/issues POT-Creation-Date: 2011-04-24 11:22+0700 PO-Revision-Date: 2011-04-24 12:15+0000 Last-Translator: Sergiy_Gavrylov Language-Team: LANGUAGE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Language: uk_UA Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2) Додати елемент дерева сайтуДодаткові коментарі для цього елементу.Додаткові налаштуванняПсевдонімОсновні налаштуванняНавігаційний ланцюжокОписНалаштування показуТочний URL чи URL-шаблон (див. «Додаткові налаштування») для цього елемента.ПрихованийПідказкаПозиція елемента між іншими елементами того ж батьківського сайту.Батько елемента залишений без змін. Елемент не може бути батьком для самого себе.МенюПеремістити внизПеремістити вгоруБатьківський сайтЕлемент дерева батьківського сайту.Коротка назва для звертання до дерева сайту з шаблону.Коротка назва для звертання до елементу з шаблона.
Зарезервовані псевдоніми: «trunk», «this-children» та «this-siblings».Показувати в навігаційному ланцюжкуПоказувати в менюПоказувати в дереві сайтуДерево сайтуЕлемент дерева сайтуЕлементи дерева сайтуДерева сайтуЗаголовок елемента дерева сайту. Може містити змінні шаблону, наприклад: {{ mytitle }}.Дерево сайту, до якого належить елемент.Додаткові дані про елемент, що будуть використовуватись як підказка.Порядок сортуванняЗаголовокДеревоURLURL як шаблонЧи заданий URL потрібно обробляти як шаблон.
Увага: Зверніться до документації Django «Диспетчер URL» (розділ «Присвоювання назв URL-шаблонам»).Чи показувати цей елемент в навігаційному ланцюжку.Чи показувати цей елемент в меню.Чи показувати цей елемент в дереві сайту.Чи приховувати цей елемент в навігації.django-sitetree-1.5.1/sitetree/locale/uk/LC_MESSAGES/django.po000066400000000000000000000144201263651575600237140ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: django-sitetree\n" "Report-Msgid-Bugs-To: https://github.com/idlesign/django-sitetree/issues\n" "POT-Creation-Date: 2011-04-24 11:22+0700\n" "PO-Revision-Date: 2011-04-24 12:15+0000\n" "Last-Translator: Sergiy_Gavrylov \n" "Language-Team: LANGUAGE \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: uk_UA\n" "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)\n" #: admin.py:16 msgid "Basic settings" msgstr "Основні налаштування" #: admin.py:19 msgid "Display settings" msgstr "Налаштування показу" #: admin.py:23 msgid "Additional settings" msgstr "Додаткові налаштування" #: admin.py:142 msgid "Item's parent left unchanged. Item couldn't be parent to itself." msgstr "" "Батько елемента залишений без змін. Елемент не може бути батьком для самого " "себе." #: models.py:23 models.py:39 msgid "Alias" msgstr "Псевдонім" #: models.py:23 msgid "Short name to address site tree from a template." msgstr "Коротка назва для звертання до дерева сайту з шаблону." #: models.py:26 models.py:37 msgid "Site Tree" msgstr "Дерево сайту" #: models.py:27 msgid "Site Trees" msgstr "Дерева сайту" #: models.py:33 templates/admin/sitetree/tree/change_form.html:38 msgid "Title" msgstr "Заголовок" #: models.py:33 msgid "" "Site tree item title. Can contain template variables E.g.: {{ mytitle }}." msgstr "" "Заголовок елемента дерева сайту. Може містити змінні шаблону, наприклад: {{ " "mytitle }}." #: models.py:34 msgid "Hint" msgstr "Підказка" #: models.py:34 msgid "Some additional information about this item that is used as a hint." msgstr "Додаткові дані про елемент, що будуть використовуватись як підказка." #: models.py:35 templates/admin/sitetree/tree/change_form.html:39 msgid "URL" msgstr "URL" #: models.py:35 msgid "Exact URL or URL pattern (see \"Additional settings\") for this item." msgstr "" "Точний URL чи URL-шаблон (див. «Додаткові налаштування») для цього елемента." #: models.py:36 msgid "URL as Pattern" msgstr "URL як шаблон" #: models.py:36 msgid "" "Whether the given URL should be treated as a pattern.
Note: Refer" " to Django \"URL dispatcher\" documentation (e.g. \"Naming URL patterns\" " "part)." msgstr "" "Чи заданий URL потрібно обробляти як шаблон.
Увага: Зверніться до" " документації Django «Диспетчер URL» (розділ «Присвоювання назв URL-" "шаблонам»)." #: models.py:37 msgid "Site tree this item belongs to." msgstr "Дерево сайту, до якого належить елемент." #: models.py:38 templates/admin/sitetree/tree/change_form.html:34 msgid "Hidden" msgstr "Прихований" #: models.py:38 msgid "Whether to show this item in navigation." msgstr "Чи приховувати цей елемент в навігації." #: models.py:39 msgid "" "Short name to address site tree item from a template.
Reserved " "aliases: \"trunk\", \"this-children\" and \"this-siblings\"." msgstr "" "Коротка назва для звертання до елементу з шаблона.
Зарезервовані " "псевдоніми: «trunk», «this-children» та «this-siblings»." #: models.py:40 msgid "Description" msgstr "Опис" #: models.py:40 msgid "Additional comments on this item." msgstr "Додаткові коментарі для цього елементу." #: models.py:41 msgid "Show in menu" msgstr "Показувати в меню" #: models.py:41 msgid "Whether to show this item in a menu." msgstr "Чи показувати цей елемент в меню." #: models.py:42 msgid "Show in breadcrumb path" msgstr "Показувати в навігаційному ланцюжку" #: models.py:42 msgid "Whether to show this item in a breadcrumb path." msgstr "Чи показувати цей елемент в навігаційному ланцюжку." #: models.py:43 msgid "Show in site tree" msgstr "Показувати в дереві сайту" #: models.py:43 msgid "Whether to show this item in a site tree." msgstr "Чи показувати цей елемент в дереві сайту." #: models.py:46 msgid "Parent" msgstr "Батьківський сайт" #: models.py:46 msgid "Parent site tree item." msgstr "Елемент дерева батьківського сайту." #: models.py:47 templates/admin/sitetree/tree/change_form.html:40 msgid "Sort order" msgstr "Порядок сортування" #: models.py:47 msgid "Item position among other site tree items under the same parent." msgstr "Позиція елемента між іншими елементами того ж батьківського сайту." #: models.py:60 msgid "Site Tree Item" msgstr "Елемент дерева сайту" #: models.py:61 templates/admin/sitetree/tree/change_form.html:17 msgid "Site Tree Items" msgstr "Елементи дерева сайту" #: templates/admin/sitetree/tree/change_form.html:24 msgid "Add Site Tree item" msgstr "Додати елемент дерева сайту" #: templates/admin/sitetree/tree/change_form.html:35 msgid "Menu" msgstr "Меню" #: templates/admin/sitetree/tree/change_form.html:36 msgid "Breadcrumbs" msgstr "Навігаційний ланцюжок" #: templates/admin/sitetree/tree/change_form.html:37 msgid "Tree" msgstr "Дерево" #: templates/admin/sitetree/tree/tree.html:16 msgid "Move up" msgstr "Перемістити вгору" #: templates/admin/sitetree/tree/tree.html:18 msgid "Move down" msgstr "Перемістити вниз" django-sitetree-1.5.1/sitetree/management/000077500000000000000000000000001263651575600205625ustar00rootroot00000000000000django-sitetree-1.5.1/sitetree/management/__init__.py000066400000000000000000000000011263651575600226620ustar00rootroot00000000000000 django-sitetree-1.5.1/sitetree/management/commands/000077500000000000000000000000001263651575600223635ustar00rootroot00000000000000django-sitetree-1.5.1/sitetree/management/commands/__init__.py000066400000000000000000000000011263651575600244630ustar00rootroot00000000000000 django-sitetree-1.5.1/sitetree/management/commands/sitetree_resync_apps.py000066400000000000000000000051261263651575600271730ustar00rootroot00000000000000from optparse import make_option from django.core.management.base import BaseCommand from django.db import DEFAULT_DB_ALIAS from sitetree.utils import get_tree_model, import_project_sitetree_modules from sitetree.settings import APP_MODULE_NAME from sitetree.sitetreeapp import Cache MODEL_TREE_CLASS = get_tree_model() class Command(BaseCommand): help = 'Places sitetrees of the project applications (defined in `app_name.sitetree.py`) into DB, ' \ 'replacing old ones if any.' args = '[app_name app_name ...]' option_list = BaseCommand.option_list + ( make_option( '--database', action='store', dest='database', default=DEFAULT_DB_ALIAS, help='Nominates a specific database to place trees and items into. Defaults to the "default" database.' ), ) def handle(self, *apps, **options): using = options.get('database', DEFAULT_DB_ALIAS) tree_modules = import_project_sitetree_modules() if not tree_modules: self.stdout.write('No sitetrees found in project apps (searched in %%app%%/%s.py).\n' % APP_MODULE_NAME) for module in tree_modules: sitetrees = getattr(module, 'sitetrees', None) app = module.__dict__['__package__'] if not apps or app in apps: if sitetrees is not None: self.stdout.write('Sitetrees found in `%s` app ...\n' % app) for tree in sitetrees: self.stdout.write(' Processing `%s` tree ...\n' % tree.alias) # Delete trees with the same name beforehand. MODEL_TREE_CLASS.objects.filter(alias=tree.alias).using(using).delete() # Drop id to let the DB handle it. tree.id = None tree.save(using=using) for item in tree.dynamic_items: self.stdout.write(' Adding `%s` tree item ...\n' % item.title) # Drop id to let the DB handle it. item.id = None if item.parent is not None: # Suppose parent tree object is already saved to DB. item.parent_id = item.parent.id item.tree = tree item.save(using=using) # Copy permissions to M2M field once `item` # has been saved item.access_permissions = item.permissions Cache.reset() django-sitetree-1.5.1/sitetree/management/commands/sitetreedump.py000066400000000000000000000035151263651575600254530ustar00rootroot00000000000000from optparse import make_option from django.core import serializers from django.core.management.base import BaseCommand, CommandError from django.db import DEFAULT_DB_ALIAS from sitetree.utils import get_tree_model, get_tree_item_model MODEL_TREE_CLASS = get_tree_model() MODEL_TREE_ITEM_CLASS = get_tree_item_model() class Command(BaseCommand): option_list = BaseCommand.option_list + ( make_option('--indent', default=None, dest='indent', type='int', help='Specifies the indent level to use when pretty-printing output.'), make_option('--items_only', action='store_true', dest='items_only', default=False, help='Export tree items only.'), make_option('--database', action='store', dest='database', default=DEFAULT_DB_ALIAS, help='Nominates a specific database to export fixtures from. ' 'Defaults to the "default" database.'), ) help = 'Output sitetrees from database as a fixture in JSON format.' args = '[tree_alias tree_alias ...]' def handle(self, *aliases, **options): indent = options.get('indent', None) using = options.get('database', DEFAULT_DB_ALIAS) items_only = options.get('items_only', False) objects = [] if aliases: trees = MODEL_TREE_CLASS._default_manager.using(using).filter(alias__in=aliases) else: trees = MODEL_TREE_CLASS._default_manager.using(using).all() if not items_only: objects.extend(trees) for tree in trees: objects.extend(MODEL_TREE_ITEM_CLASS._default_manager.using(using).filter(tree=tree).order_by('parent')) try: return serializers.serialize('json', objects, indent=indent) except Exception as e: raise CommandError('Unable to serialize sitetree(s): %s' % e) django-sitetree-1.5.1/sitetree/management/commands/sitetreeload.py000066400000000000000000000164511263651575600254300ustar00rootroot00000000000000import sys from optparse import make_option from collections import defaultdict import django from django.core import serializers from django.core.management.base import BaseCommand, CommandError from django.core.management.color import no_style from django.db import connections, router, transaction, DEFAULT_DB_ALIAS from django.core.exceptions import ObjectDoesNotExist from sitetree.utils import get_tree_model, get_tree_item_model MODEL_TREE_CLASS = get_tree_model() MODEL_TREE_ITEM_CLASS = get_tree_item_model() class Command(BaseCommand): option_list = BaseCommand.option_list + ( make_option('--database', action='store', dest='database', default=DEFAULT_DB_ALIAS, help='Nominates a specific database to load fixtures into. ' 'Defaults to the "default" database.'), make_option('--mode', action='store', dest='mode', default='append', help='Mode to put data into DB. Variants: `replace`, `append`.'), make_option('--items_into_tree', action='store', dest='into_tree', default=None, help='Import only tree items data into tree with given alias.'), ) help = 'Loads sitetrees from fixture in JSON format into database.' args = '[fixture_file fixture_file ...]' def handle(self, *fixture_files, **options): using = options.get('database', DEFAULT_DB_ALIAS) mode = options.get('mode', 'append') items_into_tree = options.get('into_tree', None) if items_into_tree is not None: try: items_into_tree = MODEL_TREE_CLASS.objects.get(alias=items_into_tree) except ObjectDoesNotExist: raise CommandError('Target tree alised by `%s` does not exist. Please create it before import.' % items_into_tree) else: mode = 'append' connection = connections[using] cursor = connection.cursor() self.style = no_style() django_version = django.get_version() django_version_less_17 = django_version < '1.7' django_version_less_18 = django_version < '1.8' if django_version_less_17: transaction.commit_unless_managed(using=using) if django_version_less_18: transaction.enter_transaction_management(using=using) if django_version_less_17: transaction.managed(True, using=using) loaded_object_count = 0 if mode == 'replace': try: MODEL_TREE_CLASS.objects.all().delete() MODEL_TREE_ITEM_CLASS.objects.all().delete() except ObjectDoesNotExist: pass for fixture_file in fixture_files: self.stdout.write('Loading fixture from `%s` ...\n' % fixture_file) fixture = open(fixture_file, 'r') try: objects = serializers.deserialize('json', fixture, using=using) except (SystemExit, KeyboardInterrupt): raise trees = [] tree_items = defaultdict(list) tree_item_parents = defaultdict(list) tree_items_new_indexes = {} try: allow_migrate = router.allow_migrate except AttributeError: # Django < 1.7 allow_migrate = router.allow_syncdb for obj in objects: if allow_migrate(using, obj.object.__class__): if isinstance(obj.object, (MODEL_TREE_CLASS, MODEL_TREE_ITEM_CLASS)): if isinstance(obj.object, MODEL_TREE_CLASS): trees.append(obj.object) else: if items_into_tree is not None: obj.object.tree_id = items_into_tree.id tree_items[obj.object.tree_id].append(obj.object) tree_item_parents[obj.object.parent_id].append(obj.object.id) if items_into_tree is not None: trees = [items_into_tree,] try: for tree in trees: self.stdout.write('\nImporting tree `%s` ...\n' % tree.alias) orig_tree_id = tree.id if items_into_tree is None: if mode == 'append': tree.pk = None tree.id = None tree.save(using=using) loaded_object_count += 1 parents_ahead = [] for tree_item in tree_items[orig_tree_id]: parent_ahead = False self.stdout.write('Importing item `%s` ...\n' % tree_item.title) tree_item.tree_id = tree.id orig_item_id = tree_item.id if mode == 'append': tree_item.pk = None tree_item.id = None if tree_item.id in tree_items_new_indexes: tree_item.pk = tree_item.id = tree_items_new_indexes[tree_item.id] if tree_item.parent_id is not None: if tree_item.parent_id in tree_items_new_indexes: tree_item.parent_id = tree_items_new_indexes[tree_item.parent_id] else: parent_ahead = True tree_item.save(using=using) loaded_object_count += 1 if mode == 'append': tree_items_new_indexes[orig_item_id] = tree_item.id if parent_ahead: parents_ahead.append(tree_item) # Second pass is necessary for tree items being imported before their parents. for tree_item in parents_ahead: tree_item.parent_id = tree_items_new_indexes[tree_item.parent_id] tree_item.save(using=using) except (SystemExit, KeyboardInterrupt): raise except Exception: import traceback fixture.close() if django_version_less_18: transaction.rollback(using=using) transaction.leave_transaction_management(using=using) self.stderr.write( self.style.ERROR('Fixture `%s` import error: %s\n' % ( fixture_file, ''.join(traceback.format_exception( sys.exc_type, sys.exc_value, sys.exc_traceback )) )) ) fixture.close() # Reset DB sequences, for DBMS with sequences support. if loaded_object_count > 0: sequence_sql = connection.ops.sequence_reset_sql(self.style, [MODEL_TREE_CLASS, MODEL_TREE_ITEM_CLASS]) if sequence_sql: self.stdout.write('Resetting DB sequences ...\n') for line in sequence_sql: cursor.execute(line) if django_version_less_18: transaction.commit(using=using) transaction.leave_transaction_management(using=using) connection.close() django-sitetree-1.5.1/sitetree/migrations/000077500000000000000000000000001263651575600206225ustar00rootroot00000000000000django-sitetree-1.5.1/sitetree/migrations/0001_initial.py000066400000000000000000000120301263651575600232610ustar00rootroot00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import models, migrations import sitetree.models class Migration(migrations.Migration): dependencies = [ ('auth', '0001_initial'), ] operations = [ migrations.CreateModel( name='Tree', fields=[ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), ('title', models.CharField(help_text='Site tree title for presentational purposes.', max_length=100, verbose_name='Title', blank=True)), ('alias', models.CharField(help_text='Short name to address site tree from templates.
Note: change with care.', unique=True, max_length=80, verbose_name='Alias', db_index=True)), ], options={ 'abstract': False, 'verbose_name': 'Site Tree', 'verbose_name_plural': 'Site Trees', }, bases=(models.Model,), ), migrations.CreateModel( name='TreeItem', fields=[ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), ('title', models.CharField(help_text='Site tree item title. Can contain template variables E.g.: {{ mytitle }}.', max_length=100, verbose_name='Title')), ('hint', models.CharField(default='', help_text='Some additional information about this item that is used as a hint.', max_length=200, verbose_name='Hint', blank=True)), ('url', models.CharField(help_text='Exact URL or URL pattern (see "Additional settings") for this item.', max_length=200, verbose_name='URL', db_index=True)), ('urlaspattern', models.BooleanField(default=False, help_text='Whether the given URL should be treated as a pattern.
Note: Refer to Django "URL dispatcher" documentation (e.g. "Naming URL patterns" part).', db_index=True, verbose_name='URL as Pattern')), ('hidden', models.BooleanField(default=False, help_text='Whether to show this item in navigation.', db_index=True, verbose_name='Hidden')), ('alias', sitetree.models.CharFieldNullable(max_length=80, blank=True, help_text='Short name to address site tree item from a template.
Reserved aliases: "trunk", "this-children", "this-siblings", "this-ancestor-children", "this-parent-siblings".', null=True, verbose_name='Alias', db_index=True)), ('description', models.TextField(default='', help_text='Additional comments on this item.', verbose_name='Description', blank=True)), ('inmenu', models.BooleanField(default=True, help_text='Whether to show this item in a menu.', db_index=True, verbose_name='Show in menu')), ('inbreadcrumbs', models.BooleanField(default=True, help_text='Whether to show this item in a breadcrumb path.', db_index=True, verbose_name='Show in breadcrumb path')), ('insitetree', models.BooleanField(default=True, help_text='Whether to show this item in a site tree.', db_index=True, verbose_name='Show in site tree')), ('access_loggedin', models.BooleanField(default=False, help_text='Check it to grant access to this item to authenticated users only.', db_index=True, verbose_name='Logged in only')), ('access_guest', models.BooleanField(default=False, help_text='Check it to grant access to this item to guests only.', db_index=True, verbose_name='Guests only')), ('access_restricted', models.BooleanField(default=False, help_text='Check it to restrict user access to this item, using Django permissions system.', db_index=True, verbose_name='Restrict access to permissions')), ('access_perm_type', models.IntegerField(default=1, help_text='Any — user should have any of chosen permissions. All — user should have all chosen permissions.', verbose_name='Permissions interpretation', choices=[(1, 'Any'), (2, 'All')])), ('sort_order', models.IntegerField(default=0, help_text='Item position among other site tree items under the same parent.', verbose_name='Sort order', db_index=True)), ('access_permissions', models.ManyToManyField(to='auth.Permission', verbose_name='Permissions granting access', blank=True)), ('parent', models.ForeignKey(related_name='treeitem_parent', blank=True, to='sitetree.TreeItem', help_text='Parent site tree item.', null=True, verbose_name='Parent')), ('tree', models.ForeignKey(related_name='treeitem_tree', verbose_name='Site Tree', to='sitetree.Tree', help_text='Site tree this item belongs to.')), ], options={ 'abstract': False, 'verbose_name': 'Site Tree Item', 'verbose_name_plural': 'Site Tree Items', }, bases=(models.Model,), ), migrations.AlterUniqueTogether( name='treeitem', unique_together=set([('tree', 'alias')]), ), ] django-sitetree-1.5.1/sitetree/migrations/__init__.py000066400000000000000000000000001263651575600227210ustar00rootroot00000000000000django-sitetree-1.5.1/sitetree/models.py000066400000000000000000000141341263651575600203060ustar00rootroot00000000000000from django.db import models from django.conf import settings from django.contrib.auth.models import Permission from django.utils.translation import ugettext_lazy as _ from django.utils.encoding import python_2_unicode_compatible from .settings import MODEL_TREE, TREE_ITEMS_ALIASES # This allows South to handle our custom 'CharFieldNullable' field. if 'south' in settings.INSTALLED_APPS: from south.modelsinspector import add_introspection_rules add_introspection_rules([], ['^sitetree\.models\.CharFieldNullable']) class CharFieldNullable(models.CharField): """We use custom char field to put nulls in SiteTreeItem 'alias' field. That allows 'unique_together' directive in Meta to work properly, so we don't have two site tree items with the same alias in the same site tree. """ def get_prep_value(self, value): if value is not None: if value.strip() == '': return None return self.to_python(value) @python_2_unicode_compatible class TreeBase(models.Model): title = models.CharField( _('Title'), max_length=100, help_text=_('Site tree title for presentational purposes.'), blank=True) alias = models.CharField( _('Alias'), max_length=80, help_text=_('Short name to address site tree from templates.
Note: change with care.'), unique=True, db_index=True) class Meta(object): abstract = True verbose_name = _('Site Tree') verbose_name_plural = _('Site Trees') def get_title(self): return self.title or self.alias def __str__(self): return self.alias @python_2_unicode_compatible class TreeItemBase(models.Model): PERM_TYPE_ANY = 1 PERM_TYPE_ALL = 2 PERM_TYPE_CHOICES = ( (PERM_TYPE_ANY, _('Any')), (PERM_TYPE_ALL, _('All')) ) title = models.CharField( _('Title'), max_length=100, help_text=_('Site tree item title. Can contain template variables E.g.: {{ mytitle }}.')) hint = models.CharField( _('Hint'), max_length=200, help_text=_('Some additional information about this item that is used as a hint.'), blank=True, default='') url = models.CharField( _('URL'), max_length=200, help_text=_('Exact URL or URL pattern (see "Additional settings") for this item.'), db_index=True) urlaspattern = models.BooleanField( _('URL as Pattern'), help_text=_('Whether the given URL should be treated as a pattern.
' 'Note: Refer to Django "URL dispatcher" documentation (e.g. "Naming URL patterns" part).'), db_index=True, default=False) tree = models.ForeignKey( MODEL_TREE, related_name='%(class)s_tree', verbose_name=_('Site Tree'), help_text=_('Site tree this item belongs to.'), db_index=True) hidden = models.BooleanField( _('Hidden'), help_text=_('Whether to show this item in navigation.'), db_index=True, default=False) alias = CharFieldNullable( _('Alias'), max_length=80, help_text=_( 'Short name to address site tree item from a template.
' 'Reserved aliases: "%s".' % '", "'.join(TREE_ITEMS_ALIASES) ), db_index=True, blank=True, null=True) description = models.TextField( _('Description'), help_text=_('Additional comments on this item.'), blank=True, default='') inmenu = models.BooleanField( _('Show in menu'), help_text=_('Whether to show this item in a menu.'), db_index=True, default=True) inbreadcrumbs = models.BooleanField( _('Show in breadcrumb path'), help_text=_('Whether to show this item in a breadcrumb path.'), db_index=True, default=True) insitetree = models.BooleanField( _('Show in site tree'), help_text=_('Whether to show this item in a site tree.'), db_index=True, default=True) access_loggedin = models.BooleanField( _('Logged in only'), help_text=_('Check it to grant access to this item to authenticated users only.'), db_index=True, default=False) access_guest = models.BooleanField( _('Guests only'), help_text=_('Check it to grant access to this item to guests only.'), db_index=True, default=False) access_restricted = models.BooleanField( _('Restrict access to permissions'), help_text=_('Check it to restrict user access to this item, using Django permissions system.'), db_index=True, default=False) access_permissions = models.ManyToManyField( Permission, verbose_name=_('Permissions granting access'), blank=True) access_perm_type = models.IntegerField( _('Permissions interpretation'), help_text=_('Any — user should have any of chosen permissions. ' 'All — user should have all chosen permissions.'), choices=PERM_TYPE_CHOICES, default=PERM_TYPE_ANY) # These two are for 'adjacency list' model. # This is the current approach of tree representation for sitetree. parent = models.ForeignKey( 'self', related_name='%(class)s_parent', verbose_name=_('Parent'), help_text=_('Parent site tree item.'), db_index=True, null=True, blank=True) sort_order = models.IntegerField( _('Sort order'), help_text=_('Item position among other site tree items under the same parent.'), db_index=True, default=0) def save(self, force_insert=False, force_update=False, **kwargs): """We override parent save method to set item's sort order to its' primary key value. """ super(TreeItemBase, self).save(force_insert, force_update, **kwargs) if self.sort_order == 0: self.sort_order = self.id self.save() class Meta(object): abstract = True verbose_name = _('Site Tree Item') verbose_name_plural = _('Site Tree Items') unique_together = ('tree', 'alias') def __str__(self): return self.title class Tree(TreeBase): """Built-in tree class. Default functionality.""" class TreeItem(TreeItemBase): """Built-in tree item class. Default functionality.""" django-sitetree-1.5.1/sitetree/runtests.py000077500000000000000000000020421263651575600207100ustar00rootroot00000000000000#! /usr/bin/env python import sys import os from django.conf import settings, global_settings APP_NAME = 'sitetree' def main(): sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..')) if not settings.configured: settings.configure( INSTALLED_APPS=('django.contrib.auth', 'django.contrib.contenttypes', APP_NAME), DATABASES={'default': {'ENGINE': 'django.db.backends.sqlite3'}}, ROOT_URLCONF = 'sitetree.tests', MIDDLEWARE_CLASSES=global_settings.MIDDLEWARE_CLASSES, # Prevents Django 1.7 warning. TEMPLATE_CONTEXT_PROCESSORS=tuple(global_settings.TEMPLATE_CONTEXT_PROCESSORS) + ( 'django.core.context_processors.request', ) ) try: # Django 1.7 + from django import setup setup() except ImportError: pass from django.test.utils import get_runner runner = get_runner(settings)() failures = runner.run_tests((APP_NAME,)) sys.exit(failures) if __name__ == '__main__': main() django-sitetree-1.5.1/sitetree/settings.py000066400000000000000000000020751263651575600206640ustar00rootroot00000000000000from django.conf import settings MODEL_TREE = getattr(settings, 'SITETREE_MODEL_TREE', 'sitetree.Tree') MODEL_TREE_ITEM = getattr(settings, 'SITETREE_MODEL_TREE_ITEM', 'sitetree.TreeItem') APP_MODULE_NAME = getattr(settings, 'SITETREE_APP_MODULE_NAME', 'sitetrees') UNRESOLVED_ITEM_MARKER = getattr(settings, 'SITETREE_UNRESOLVED_ITEM_MARKER', u'#unresolved') RAISE_ITEMS_ERRORS_ON_DEBUG = getattr(settings, 'SITETREE_RAISE_ITEMS_ERRORS_ON_DEBUG', True) # Sitetree objects are stored in Django cache for a year (60 * 60 * 24 * 365 = 31536000 sec). # Cache is only invalidated on sitetree or sitetree item change. CACHE_TIMEOUT = getattr(settings, 'SITETREE_CACHE_TIMEOUT', 31536000) # Reserved tree items aliases. ALIAS_TRUNK = 'trunk' ALIAS_THIS_CHILDREN = 'this-children' ALIAS_THIS_SIBLINGS = 'this-siblings' ALIAS_THIS_ANCESTOR_CHILDREN = 'this-ancestor-children' ALIAS_THIS_PARENT_SIBLINGS = 'this-parent-siblings' TREE_ITEMS_ALIASES = [ ALIAS_TRUNK, ALIAS_THIS_CHILDREN, ALIAS_THIS_SIBLINGS, ALIAS_THIS_ANCESTOR_CHILDREN, ALIAS_THIS_PARENT_SIBLINGS ] django-sitetree-1.5.1/sitetree/sitetreeapp.py000066400000000000000000001040131263651575600213440ustar00rootroot00000000000000from __future__ import unicode_literals import warnings from collections import defaultdict from copy import copy, deepcopy from threading import local from functools import partial from django.conf import settings from django import VERSION from django.core.cache import cache from django.db.models import signals from django.utils import six from django.utils.http import urlquote from django.utils.translation import get_language from django.utils.encoding import python_2_unicode_compatible from django.template import Context from django.template.loader import get_template from django.template.base import ( FilterExpression, Lexer, Parser, Token, Variable, VariableDoesNotExist, TOKEN_BLOCK, UNKNOWN_SOURCE, TOKEN_TEXT, TOKEN_VAR, VARIABLE_TAG_START) from django.template.defaulttags import url as url_tag from .utils import get_tree_model, get_tree_item_model, import_app_sitetree_module, generate_id_for from .settings import ( ALIAS_TRUNK, ALIAS_THIS_CHILDREN, ALIAS_THIS_SIBLINGS, ALIAS_THIS_PARENT_SIBLINGS, ALIAS_THIS_ANCESTOR_CHILDREN, UNRESOLVED_ITEM_MARKER, RAISE_ITEMS_ERRORS_ON_DEBUG, CACHE_TIMEOUT) if VERSION >= (1, 9, 0): get_lexer = partial(Lexer) else: get_lexer = partial(Lexer, origin=UNKNOWN_SOURCE) MODEL_TREE_CLASS = get_tree_model() MODEL_TREE_ITEM_CLASS = get_tree_item_model() # Holds tree items processor callable or None. _ITEMS_PROCESSOR = None # Holds aliases of trees that support internationalization. _I18N_TREES = [] # Holds trees dynamically loaded from project apps. _DYNAMIC_TREES = {} # Dictionary index in `_DYNAMIC_TREES` for orphaned trees list. _IDX_ORPHAN_TREES = 'orphans' # Dictinary index name template in `_DYNAMIC_TREES`. _IDX_TPL = '%s|:|%s' # SiteTree app-wise object. _SITETREE = None _THREAD_LOCAL = local() _THREAD_LANG = 'sitetree_lang' def get_sitetree(): """Returns SiteTree [singleton] object, implementing utility methods. :return: SiteTree """ global _SITETREE if _SITETREE is None: _SITETREE = SiteTree() return _SITETREE def register_items_hook(callable): """Registers a hook callable to process tree items right before they are passed to templates. Callable should be able to: a) handle ``tree_items`` and ``tree_sender`` key params. ``tree_items`` will contain a list of extended TreeItem objects ready to pass to template. ``tree_sender`` will contain navigation type identifier (e.g.: `menu`, `sitetree`, `breadcrumbs`, `menu.children`, `sitetree.children`) b) return a list of extended TreeItems objects to pass to template. Example:: # Put the following code somewhere where it'd be triggered as expected. E.g. in app view.py. # First import the register function. from sitetree.sitetreeapp import register_items_hook # The following function will be used as items processor. def my_items_processor(tree_items, tree_sender): # Suppose we want to process only menu child items. if tree_sender == 'menu.children': # Lets add 'Hooked: ' to resolved titles of every item. for item in tree_items: item.title_resolved = 'Hooked: %s' % item.title_resolved # Return items list mutated or not. return tree_items # And we register items processor. register_items_hook(my_items_processor) """ global _ITEMS_PROCESSOR _ITEMS_PROCESSOR = callable def register_i18n_trees(aliases): """Registers aliases of internationalized sitetrees. Internationalized sitetrees are those, which are dubbed by other trees having locale identifying suffixes in their aliases. Lets suppose ``my_tree`` is the alias of a generic tree. This tree is the one that we call by its alias in templates, and it is the one which is used if no i18n version of that tree is found. Given that ``my_tree_en``, ``my_tree_ru`` and other ``my_tree_{locale-id}``-like trees are considered internationalization sitetrees. These are used (if available) in accordance with current locale used by project. Example:: # Put the following code somewhere where it'd be triggered as expected. E.g. in main urls.py. # First import the register function. from sitetree.sitetreeapp import register_i18n_trees # At last we register i18n trees. register_i18n_trees(['my_tree', 'my_another_tree']) """ global _I18N_TREES _I18N_TREES = aliases def register_dynamic_trees(trees, *args, **kwargs): """Registers dynamic trees to be available for `sitetree` runtime. Expects `trees` to be an iterable with structures created with `compose_dynamic_tree()`. Example:: register_dynamic_trees( # Get all the trees from `my_app`. compose_dynamic_tree('my_app'), # Get all the trees from `my_app` and attach them to `main` tree root. compose_dynamic_tree('my_app', target_tree_alias='main'), # Get all the trees from `my_app` and attach them to `has_dynamic` aliased item in `main` tree. compose_dynamic_tree('articles', target_tree_alias='main', parent_tree_item_alias='has_dynamic'), # Define a tree right on the registration. compose_dynamic_tree(( tree('dynamic', items=( item('dynamic_1', 'dynamic_1_url', children=( item('dynamic_1_sub_1', 'dynamic_1_sub_1_url'), )), item('dynamic_2', 'dynamic_2_url'), )), )), ) Accepted kwargs: :param bool reset_cache: Resets tree cache, to introduce all changes made to a tree immediately. """ global _DYNAMIC_TREES if _IDX_ORPHAN_TREES not in _DYNAMIC_TREES: _DYNAMIC_TREES[_IDX_ORPHAN_TREES] = {} if isinstance(trees, dict): # New `less-brackets` style registration. trees = [trees] trees.extend(args) for tree in trees: if tree is not None and tree['sitetrees'] is not None: if tree['tree'] is None: # Register trees as they are defined in app. for st in tree['sitetrees']: if st.alias not in _DYNAMIC_TREES[_IDX_ORPHAN_TREES]: _DYNAMIC_TREES[_IDX_ORPHAN_TREES][st.alias] = [] _DYNAMIC_TREES[_IDX_ORPHAN_TREES][st.alias].append(st) else: # Register tree items as parts of existing trees. index = _IDX_TPL % (tree['tree'], tree['parent_item']) if index not in _DYNAMIC_TREES: _DYNAMIC_TREES[index] = [] _DYNAMIC_TREES[index].extend(tree['sitetrees']) reset_cache = kwargs.get('reset_cache', False) if reset_cache: cache = get_sitetree().cache cache.empty() cache.reset() def get_dynamic_trees(): """Returns a dictionary with currently registered dynamic trees.""" return _DYNAMIC_TREES def compose_dynamic_tree(src, target_tree_alias=None, parent_tree_item_alias=None, include_trees=None): """Returns a structure describing a dynamic sitetree.utils The structure can be built from various sources, Thus, if a string is passed to `src`, it'll be treated as the name of an app, from where one want to import sitetrees definitions. On the other hand, `src` can be an iterable of trees definitions (see `sitetree.utils.tree()` and `item()` functions). `target_tree_alias` - expects a static tree alias to attach items from dynamic trees to. `parent_tree_item_alias` - expects a tree item alias from a static tree to attach items from dynamic trees to. `include_trees` - expects a list of sitetree aliases to filter `src`. """ def result(sitetrees=src): if include_trees is not None: sitetrees = [tree for tree in sitetrees if tree.alias in include_trees] return {'app': src, 'sitetrees': sitetrees, 'tree': target_tree_alias, 'parent_item': parent_tree_item_alias} if isinstance(src, six.string_types): # Considered an application name. try: module = import_app_sitetree_module(src) if module is None: return None return result(getattr(module, 'sitetrees', None)) except ImportError as e: if settings.DEBUG: warnings.warn('Unable to register dynamic sitetree(s) for `%s` application: %s. ' % (src, e)) else: return result() return None @python_2_unicode_compatible class LazyTitle(object): """Lazily resolves any variable found in a title of an item. Produces resolved title as unicode representation.""" def __init__(self, title): self.title = title def __str__(self): my_lexer = get_lexer(self.title) my_tokens = my_lexer.tokenize() # Deliberately strip off template tokens that are not text or variable. for my_token in my_tokens: if my_token.token_type not in (TOKEN_TEXT, TOKEN_VAR): my_tokens.remove(my_token) my_parser = Parser(my_tokens) return my_parser.parse().render(SiteTree.get_global_context()) def __eq__(self, other): return self.__str__() == other class Cache(object): """Contains cache-related stuff.""" def __init__(self): self.cache = None cache_empty = self.empty # Listen for signals from the models. signals.post_save.connect(cache_empty, sender=MODEL_TREE_CLASS) signals.post_save.connect(cache_empty, sender=MODEL_TREE_ITEM_CLASS) signals.post_delete.connect(cache_empty, sender=MODEL_TREE_ITEM_CLASS) # Listen to the changes in item permissions table. signals.m2m_changed.connect(cache_empty, sender=MODEL_TREE_ITEM_CLASS.access_permissions) @classmethod def reset(cls): """Instructs sitetree to drop and recreate cache. Could be used to show up tree changes made in a different process. """ cache.get('sitetrees_reset', True) def init(self): """Initializes local cache from Django cache.""" # Drop cache flag set by .reset() method. cache.get('sitetrees_reset') and self.empty() cache_ = cache.get('sitetrees') if cache_ is None: # Init cache dictionary with predefined entries. cache_ = {'sitetrees': {}, 'urls': {}, 'parents': {}, 'items_by_ids': {}, 'tree_aliases': {}} self.cache = cache_ def save(self): """Saves sitetree data to Django cache.""" cache.set('sitetrees', self.cache, CACHE_TIMEOUT) def empty(self, **kwargs): """Empties cached sitetree data.""" self.cache = None cache.delete('sitetrees') cache.delete('sitetrees_reset') def get_entry(self, entry_name, key): """Returns cache entry parameter value by its name.""" return self.cache[entry_name].get(key, False) def update_entry_value(self, entry_name, key, value): """Updates cache entry parameter with new data.""" if key not in self.cache[entry_name]: self.cache[entry_name][key] = {} self.cache[entry_name][key].update(value) def set_entry(self, entry_name, key, value): """Replaces entire cache entry parameter data by its name with new data.""" self.cache[entry_name][key] = value class SiteTree(object): _global_context = Context() def __init__(self): self.cache = Cache() @classmethod def set_global_context(cls, context): """Saves context as global context if not already set or if changed. Almost all variables are resolved against global context. """ if not cls._global_context or id(context) != id(cls._global_context): cls._global_context = context @classmethod def get_global_context(cls): """Returns current sitetree global context.""" return cls._global_context def resolve_tree_i18n_alias(self, alias): """Resolves internationalized tree alias. Verifies whether a separate sitetree is available for currently active language. If so, returns i18n alias. If not, returns the initial alias. """ if alias in _I18N_TREES: current_language_code = self.lang_get().replace('_', '-').split('-')[0] i18n_tree_alias = '%s_%s' % (alias, current_language_code) trees_count = self.cache.get_entry('tree_aliases', i18n_tree_alias) if trees_count is False: trees_count = MODEL_TREE_CLASS.objects.filter(alias=i18n_tree_alias).count() self.cache.set_entry('tree_aliases', i18n_tree_alias, trees_count) if trees_count: alias = i18n_tree_alias return alias @staticmethod def attach_dynamic_tree_items(tree_alias, src_tree_items): """Attaches dynamic sitetrees items registered with `register_dynamic_trees()` to an initial (source) items list. """ if not _DYNAMIC_TREES: return src_tree_items # This guarantees that a dynamic source stays intact, # no matter how dynamic sitetrees are attached. TREES = deepcopy(_DYNAMIC_TREES) items = [] if not src_tree_items: if _IDX_ORPHAN_TREES in TREES and tree_alias in TREES[_IDX_ORPHAN_TREES]: for tree in TREES[_IDX_ORPHAN_TREES][tree_alias]: items.extend(tree.dynamic_items) else: # TODO Seems to be underoptimized %) # Tree item attachment by alias. for static_item in list(src_tree_items): items.append(static_item) if static_item.alias: idx = _IDX_TPL % (tree_alias, static_item.alias) if idx in TREES: for tree in TREES[idx]: tree.alias = tree_alias for dyn_item in tree.dynamic_items: if dyn_item.parent is None: dyn_item.parent = static_item # Unique IDs are required for the same trees attached # to different parents. dyn_item.id = generate_id_for(dyn_item) items.append(dyn_item) # Tree root attachment. idx = _IDX_TPL % (tree_alias, None) if idx in _DYNAMIC_TREES: TREES = deepcopy(_DYNAMIC_TREES) for tree in TREES[idx]: tree.alias = tree_alias items.extend(tree.dynamic_items) return items def current_app_is_admin(self): """Returns boolean whether current application is Admin contrib.""" current_app = ( getattr(self._global_context.get('request', None), 'current_app', self._global_context.current_app)) return current_app == 'admin' def get_sitetree(self, alias): """Gets site tree items from the given site tree. Caches result to dictionary. Returns (tree alias, tree items) tuple. """ # Cache aliases for speedup. cache = self.cache get_cache_entry = cache.get_entry set_cache_entry = cache.set_entry cache.init() sitetree_needs_caching = False if not self.current_app_is_admin(): # We do not need i18n for a tree rendered in Admin dropdown. alias = self.resolve_tree_i18n_alias(alias) sitetree = get_cache_entry('sitetrees', alias) if not sitetree: sitetree = MODEL_TREE_ITEM_CLASS.objects.select_related('parent', 'tree').\ filter(tree__alias__exact=alias).order_by('parent__sort_order', 'sort_order') sitetree = self.attach_dynamic_tree_items(alias, sitetree) set_cache_entry('sitetrees', alias, sitetree) sitetree_needs_caching = True parents = get_cache_entry('parents', alias) if not parents: parents = defaultdict(list) for item in sitetree: parent = getattr(item, 'parent') parents[parent].append(item) set_cache_entry('parents', alias, parents) # Prepare items by ids cache if needed. if sitetree_needs_caching: # We need this extra pass to avoid future problems on items depth calculation. for item in sitetree: cache.update_entry_value('items_by_ids', alias, {item.id: item}) for item in sitetree: if sitetree_needs_caching: item.has_children = False if not hasattr(item, 'depth'): item.depth = self.calculate_item_depth(alias, item.id) item.depth_range = range(item.depth) # Resolve item permissions. if item.access_restricted: permissions_src = ( item.permissions if getattr(item, 'is_dynamic', False) else item.access_permissions.select_related()) item.perms = set( ['%s.%s' % (perm.content_type.app_label, perm.codename) for perm in permissions_src]) # Contextual properties. item.url_resolved = self.url(item) if VARIABLE_TAG_START in item.title: item.title_resolved = LazyTitle(item.title) else: item.title_resolved = item.title item.is_current = False item.in_current_branch = False # Get current item for the given sitetree. self.get_tree_current_item(alias) # Save sitetree data into cache if needed. if sitetree_needs_caching: cache.save() return alias, sitetree def calculate_item_depth(self, tree_alias, item_id, depth=0): """Calculates depth of the item in the tree.""" item = self.get_item_by_id(tree_alias, item_id) if not hasattr(item, 'depth'): if item.parent is not None: depth = self.calculate_item_depth(tree_alias, item.parent.id, depth + 1) else: depth = item.depth + depth return depth def get_item_by_id(self, tree_alias, item_id): """Get the item from the tree by its ID.""" return self.cache.get_entry('items_by_ids', tree_alias)[item_id] def get_tree_current_item(self, tree_alias): """Resolves current tree item of 'tree_alias' tree matching current request path against URL of given tree item. """ if self.current_app_is_admin(): return None current_item = None if 'request' not in self._global_context: if settings.DEBUG: raise SiteTreeError( 'Sitetree needs "django.core.context_processors.request" to be in TEMPLATE_CONTEXT_PROCESSORS ' 'in your settings file. If it is, check that your view pushes request data into the template.') else: # urlquote is an attempt to support non-ascii in url. current_url = urlquote(self._global_context['request'].path) urls_cache = self.cache.get_entry('urls', '%s%s' % (tree_alias, self.lang_get())) if urls_cache: for url_item in urls_cache: urls_cache[url_item][1].is_current = False if urls_cache[url_item][0] == current_url: current_item = urls_cache[url_item][1] if current_item is not None: current_item.is_current = True return current_item @classmethod def lang_get(cls): """Returns language code for current thread. :return: """ return getattr(_THREAD_LOCAL, _THREAD_LANG, '') or cls.lang_init() @classmethod def lang_init(cls): """Initializes and returns language code for current thread. :return: """ lang = get_language() setattr(_THREAD_LOCAL, _THREAD_LANG, lang) return lang def url(self, sitetree_item, context=None): """Resolves item's URL. 'sitetree_item' points to TreeItem object, 'url' property of which is processed as URL pattern or simple URL. """ if context is None: context = self._global_context if not isinstance(sitetree_item, MODEL_TREE_ITEM_CLASS): sitetree_item = self.resolve_var(sitetree_item, context) # Resolve only if item's URL is marked as pattern. if sitetree_item.urlaspattern: url = sitetree_item.url view_path = url all_arguments = [] if ' ' in url: view_path = url.split(' ') # We should try to resolve URL parameters from site tree item. for view_argument in view_path[1:]: resolved = self.resolve_var(view_argument) # In case of non-ascii data we leave variable unresolved. if isinstance(resolved, six.text_type): if resolved.encode('ascii', 'ignore').decode('ascii') != resolved: resolved = view_argument # We enclose arg in double quotes as already resolved. all_arguments.append('"%s"' % str(resolved)) view_path = view_path[0].strip('"\' ') if VERSION >= (1, 5, 0): # "new-style" url tag - consider sitetree named urls literals. view_path = "'%s'" % view_path url_pattern = u'%s %s' % (view_path, ' '.join(all_arguments)) else: url_pattern = str(sitetree_item.url) # i18n_patterns compatibility organized using compound cache key. cache_key = '%s%s' % (sitetree_item.tree.alias, self.lang_get()) entry_from_cache = self.cache.get_entry('urls', cache_key) if not entry_from_cache: # Create 'cache_urls' for this tree. entry_from_cache = {} self.cache.set_entry('urls', cache_key, {}) if url_pattern in entry_from_cache: resolved_url = entry_from_cache[url_pattern][0] else: if sitetree_item.urlaspattern: # Form token to pass to Django 'url' tag. url_token = u'url %s as item.url_resolved' % url_pattern url_tag( Parser(None), Token(token_type=TOKEN_BLOCK, contents=url_token) ).render(context) # We make an anchor link from an unresolved URL as a reminder. if not context['item.url_resolved']: resolved_url = UNRESOLVED_ITEM_MARKER else: resolved_url = context['item.url_resolved'] else: resolved_url = url_pattern self.cache.update_entry_value('urls', cache_key, {url_pattern: (resolved_url, sitetree_item)}) return resolved_url def init_tree(self, tree_alias, context): """Tries to initialize sitetree in memory. Returns tuple with resolved tree alias and items on success. On fail returns False. """ # Current context we will consider global. self.set_global_context(context) # Initialize language to use it in current thread. self.lang_init() # Resolve tree_alias from the context. tree_alias = self.resolve_var(tree_alias) # Get tree. tree_alias, sitetree_items = self.get_sitetree(tree_alias) # No items in tree, fail silently. if not sitetree_items: return False, False return tree_alias, sitetree_items def get_current_page_title(self, tree_alias, context): """Returns resolved from sitetree title for current page.""" return self.get_current_page_attr('title_resolved', tree_alias, context) def get_current_page_attr(self, attr_name, tree_alias, context): """Returns an arbitrary attribute of a sitetree item resolved as current for current page.""" tree_alias, sitetree_items = self.init_tree(tree_alias, context) current_item = self.get_tree_current_item(tree_alias) # Current item is unresolved, fail silently. if current_item is None: if settings.DEBUG and RAISE_ITEMS_ERRORS_ON_DEBUG: raise SiteTreeError( 'Unable to resolve current sitetree item to get a `%s` for current page. Check whether ' 'there is an appropriate sitetree item defined for current URL.' % attr_name) return '' return getattr(current_item, attr_name, '') def get_ancestor_level(self, current_item, deep=1): """Returns ancestor of level `deep` recursively""" if current_item.parent is not None: if deep <= 1: return current_item.parent else: return self.get_ancestor_level(current_item.parent, deep=deep-1) else: return current_item def menu(self, tree_alias, tree_branches, context): """Builds and returns menu structure for 'sitetree_menu' tag.""" tree_alias, sitetree_items = self.init_tree(tree_alias, context) # No items in tree, fail silently. if not sitetree_items: return '' tree_branches = self.resolve_var(tree_branches) parent_isnull = False parent_ids = [] parent_aliases = [] current_item = self.get_tree_current_item(tree_alias) self.tree_climber(tree_alias, current_item) # Support item addressing both through identifiers and aliases. for branch_id in tree_branches.split(','): branch_id = branch_id.strip() if branch_id == ALIAS_TRUNK: parent_isnull = True elif branch_id == ALIAS_THIS_CHILDREN and current_item is not None: branch_id = current_item.id parent_ids.append(branch_id) elif branch_id == ALIAS_THIS_ANCESTOR_CHILDREN and current_item is not None: branch_id = self.get_ancestor_item(tree_alias, current_item).id parent_ids.append(branch_id) elif branch_id == ALIAS_THIS_SIBLINGS and current_item is not None and current_item.parent is not None: branch_id = current_item.parent.id parent_ids.append(branch_id) elif branch_id == ALIAS_THIS_PARENT_SIBLINGS and current_item is not None: branch_id = self.get_ancestor_level(current_item, deep=2).id parent_ids.append(branch_id) elif branch_id.isdigit(): parent_ids.append(int(branch_id)) else: parent_aliases.append(branch_id) menu_items = [] for item in sitetree_items: if not item.hidden and item.inmenu and self.check_access(item, context): if item.parent is None: if parent_isnull: menu_items.append(item) else: if item.parent.id in parent_ids or item.parent.alias in parent_aliases: menu_items.append(item) # Parse titles for variables. menu_items = self.apply_hook(menu_items, 'menu') menu_items = self.update_has_children(tree_alias, menu_items, 'menu') return menu_items def apply_hook(self, items, sender): """Applies item processing hook, registered with ``register_item_hook()`` to items supplied, and returns processed list. Returns initial items list if no hook is registered. """ if _ITEMS_PROCESSOR is None: return items return _ITEMS_PROCESSOR(tree_items=items, tree_sender=sender) def check_access(self, item, context): """Checks whether a current user has an access to a certain item.""" authenticated = self._global_context['request'].user.is_authenticated() if item.access_loggedin and not authenticated: return False if item.access_guest and authenticated: return False if item.access_restricted: user_perms = set(context['user'].get_all_permissions()) if item.access_perm_type == MODEL_TREE_ITEM_CLASS.PERM_TYPE_ALL: if len(item.perms) != len(item.perms.intersection(user_perms)): return False else: if not len(item.perms.intersection(user_perms)): return False return True def breadcrumbs(self, tree_alias, context): """Builds and returns breadcrumb trail structure for 'sitetree_breadcrumbs' tag.""" tree_alias, sitetree_items = self.init_tree(tree_alias, context) # No items in tree, fail silently. if not sitetree_items: return '' current_item = self.get_tree_current_item(tree_alias) self.cache_breadcrumbs = [] if current_item is not None: self.breadcrumbs_climber(tree_alias, current_item) self.cache_breadcrumbs.reverse() items = self.apply_hook(self.cache_breadcrumbs, 'breadcrumbs') items = self.update_has_children(tree_alias, items, 'breadcrumbs') return items def tree(self, tree_alias, context): """Builds and returns tree structure for 'sitetree_tree' tag.""" tree_alias, sitetree_items = self.init_tree(tree_alias, context) # No items in tree, fail silently. if not sitetree_items: return '' tree_items = self.filter_items(self.get_children(tree_alias, None), 'sitetree') tree_items = self.apply_hook(tree_items, 'sitetree') tree_items = self.update_has_children(tree_alias, tree_items, 'sitetree') return tree_items def children(self, parent_item, navigation_type, use_template, context): """Builds and returns site tree item children structure for 'sitetree_children' tag. """ # Resolve parent item and current tree alias. parent_item = self.resolve_var(parent_item, context) tree_alias, tree_items = self.get_sitetree(parent_item.tree.alias) # Mark path to current item. self.tree_climber(tree_alias, self.get_tree_current_item(tree_alias)) tree_items = self.get_children(tree_alias, parent_item) tree_items = self.filter_items(tree_items, navigation_type) tree_items = self.apply_hook(tree_items, '%s.children' % navigation_type) tree_items = self.update_has_children(tree_alias, tree_items, navigation_type) my_template = get_template(use_template) context.update({'sitetree_items': tree_items}) return my_template.render(context) def get_children(self, tree_alias, item): if not self.current_app_is_admin(): # We do not need i18n for a tree rendered in Admin dropdown. tree_alias = self.resolve_tree_i18n_alias(tree_alias) return self.cache.get_entry('parents', tree_alias)[item] def update_has_children(self, tree_alias, tree_items, navigation_type): """Updates 'has_children' attribute for tree items.""" items = [] for tree_item in tree_items: children = self.get_children(tree_alias, tree_item) children = self.filter_items(children, navigation_type) children = self.apply_hook(children, '%s.has_children' % navigation_type) tree_item.has_children = len(children) > 0 items.append(tree_item) return items def filter_items(self, items, navigation_type=None): """Filters site tree item's children if hidden and by navigation type. NB: We do not apply any filters to sitetree in admin app. """ items_out = copy(items) if not self.current_app_is_admin(): for item in items: no_access = not self.check_access(item, self._global_context) hidden_for_nav_type = navigation_type is not None and not getattr(item, 'in' + navigation_type, False) if item.hidden or no_access or hidden_for_nav_type: items_out.remove(item) return items_out def get_ancestor_item(self, tree_alias, start_from): """Climbs up the site tree to resolve root item for chosen one.""" parent = None if hasattr(start_from, 'parent') and start_from.parent is not None: parent = self.get_ancestor_item(tree_alias, self.get_item_by_id(tree_alias, start_from.parent.id)) if parent is None: return start_from return parent def tree_climber(self, tree_alias, start_from): """Climbs up the site tree to mark items of current branch.""" if start_from is not None: start_from.in_current_branch = True if hasattr(start_from, 'parent') and start_from.parent is not None: self.tree_climber(tree_alias, self.get_item_by_id(tree_alias, start_from.parent.id)) def breadcrumbs_climber(self, tree_alias, start_from): """Climbs up the site tree to build breadcrumb path.""" if start_from.inbreadcrumbs and start_from.hidden == False and self.check_access(start_from, self._global_context): self.cache_breadcrumbs.append(start_from) if hasattr(start_from, 'parent') and start_from.parent is not None: self.breadcrumbs_climber(tree_alias, self.get_item_by_id(tree_alias, start_from.parent.id)) def resolve_var(self, varname, context=None): """Tries to resolve name as a variable in a given context. If no context specified 'global_context' is considered as context. """ if context is None: context = self._global_context if isinstance(varname, FilterExpression): varname = varname.resolve(context) else: varname = varname.strip() try: varname = Variable(varname).resolve(context) except VariableDoesNotExist: varname = varname return varname class SiteTreeError(Exception): """Exception class for sitetree application.""" django-sitetree-1.5.1/sitetree/south_migrations/000077500000000000000000000000001263651575600220445ustar00rootroot00000000000000django-sitetree-1.5.1/sitetree/south_migrations/0001_initial.py000066400000000000000000000110771263651575600245150ustar00rootroot00000000000000# encoding: utf-8 import datetime from south.db import db from south.v2 import SchemaMigration from django.db import models class Migration(SchemaMigration): def forwards(self, orm): # Adding model 'Tree' db.create_table('sitetree_tree', ( ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), ('alias', self.gf('django.db.models.fields.CharField')(unique=True, max_length=80, db_index=True)), )) db.send_create_signal('sitetree', ['Tree']) # Adding model 'TreeItem' db.create_table('sitetree_treeitem', ( ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), ('title', self.gf('django.db.models.fields.CharField')(max_length=100)), ('hint', self.gf('django.db.models.fields.CharField')(default='', max_length=200, blank=True)), ('url', self.gf('django.db.models.fields.CharField')(max_length=200, db_index=True)), ('urlaspattern', self.gf('django.db.models.fields.BooleanField')(default=False, db_index=True)), ('tree', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['sitetree.Tree'])), ('hidden', self.gf('django.db.models.fields.BooleanField')(default=False, db_index=True)), ('alias', self.gf('sitetree.models.CharFieldNullable')(db_index=True, max_length=80, null=True, blank=True)), ('description', self.gf('django.db.models.fields.TextField')(default='', blank=True)), ('inmenu', self.gf('django.db.models.fields.BooleanField')(default=True, db_index=True)), ('inbreadcrumbs', self.gf('django.db.models.fields.BooleanField')(default=True, db_index=True)), ('insitetree', self.gf('django.db.models.fields.BooleanField')(default=True, db_index=True)), ('parent', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['sitetree.TreeItem'], null=True, blank=True)), ('sort_order', self.gf('django.db.models.fields.IntegerField')(default=0, db_index=True)), )) db.send_create_signal('sitetree', ['TreeItem']) # Adding unique constraint on 'TreeItem', fields ['tree', 'alias'] db.create_unique('sitetree_treeitem', ['tree_id', 'alias']) def backwards(self, orm): # Removing unique constraint on 'TreeItem', fields ['tree', 'alias'] db.delete_unique('sitetree_treeitem', ['tree_id', 'alias']) # Deleting model 'Tree' db.delete_table('sitetree_tree') # Deleting model 'TreeItem' db.delete_table('sitetree_treeitem') models = { 'sitetree.tree': { 'Meta': {'object_name': 'Tree'}, 'alias': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80', 'db_index': 'True'}), 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) }, 'sitetree.treeitem': { 'Meta': {'unique_together': "(('tree', 'alias'),)", 'object_name': 'TreeItem'}, 'alias': ('sitetree.models.CharFieldNullable', [], {'db_index': 'True', 'max_length': '80', 'null': 'True', 'blank': 'True'}), 'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}), 'hidden': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}), 'hint': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '200', 'blank': 'True'}), 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 'inbreadcrumbs': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True'}), 'inmenu': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True'}), 'insitetree': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True'}), 'parent': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['sitetree.TreeItem']", 'null': 'True', 'blank': 'True'}), 'sort_order': ('django.db.models.fields.IntegerField', [], {'default': '0', 'db_index': 'True'}), 'title': ('django.db.models.fields.CharField', [], {'max_length': '100'}), 'tree': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['sitetree.Tree']"}), 'url': ('django.db.models.fields.CharField', [], {'max_length': '200', 'db_index': 'True'}), 'urlaspattern': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}) } } complete_apps = ['sitetree'] 0002_auto__add_field_treeitem_access_restricted__add_field_treeitem_access_.py000066400000000000000000000120071263651575600412210ustar00rootroot00000000000000django-sitetree-1.5.1/sitetree/south_migrations# encoding: utf-8 import datetime from south.db import db from south.v2 import SchemaMigration from django.db import models class Migration(SchemaMigration): def forwards(self, orm): # Adding field 'TreeItem.access_restricted' db.add_column('sitetree_treeitem', 'access_restricted', self.gf('django.db.models.fields.BooleanField')(default=False, db_index=True), keep_default=False) # Adding field 'TreeItem.access_perm_type' db.add_column('sitetree_treeitem', 'access_perm_type', self.gf('django.db.models.fields.IntegerField')(default=1), keep_default=False) # Adding M2M table for field access_permissions on 'TreeItem' db.create_table('sitetree_treeitem_access_permissions', ( ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)), ('treeitem', models.ForeignKey(orm['sitetree.treeitem'], null=False)), ('permission', models.ForeignKey(orm['auth.permission'], null=False)) )) db.create_unique('sitetree_treeitem_access_permissions', ['treeitem_id', 'permission_id']) def backwards(self, orm): # Deleting field 'TreeItem.access_restricted' db.delete_column('sitetree_treeitem', 'access_restricted') # Deleting field 'TreeItem.access_perm_type' db.delete_column('sitetree_treeitem', 'access_perm_type') # Removing M2M table for field access_permissions on 'TreeItem' db.delete_table('sitetree_treeitem_access_permissions') models = { 'auth.permission': { 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) }, 'contenttypes.contenttype': { 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) }, 'sitetree.tree': { 'Meta': {'object_name': 'Tree'}, 'alias': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80', 'db_index': 'True'}), 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) }, 'sitetree.treeitem': { 'Meta': {'unique_together': "(('tree', 'alias'),)", 'object_name': 'TreeItem'}, 'access_perm_type': ('django.db.models.fields.IntegerField', [], {'default': '1'}), 'access_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), 'access_restricted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}), 'alias': ('sitetree.models.CharFieldNullable', [], {'db_index': 'True', 'max_length': '80', 'null': 'True', 'blank': 'True'}), 'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}), 'hidden': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}), 'hint': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '200', 'blank': 'True'}), 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 'inbreadcrumbs': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True'}), 'inmenu': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True'}), 'insitetree': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True'}), 'parent': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['sitetree.TreeItem']", 'null': 'True', 'blank': 'True'}), 'sort_order': ('django.db.models.fields.IntegerField', [], {'default': '0', 'db_index': 'True'}), 'title': ('django.db.models.fields.CharField', [], {'max_length': '100'}), 'tree': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['sitetree.Tree']"}), 'url': ('django.db.models.fields.CharField', [], {'max_length': '200', 'db_index': 'True'}), 'urlaspattern': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}) } } complete_apps = ['sitetree'] django-sitetree-1.5.1/sitetree/south_migrations/0003_auto__add_field_treeitem_access_loggedin.py000066400000000000000000000102701263651575600332310ustar00rootroot00000000000000# encoding: utf-8 import datetime from south.db import db from south.v2 import SchemaMigration from django.db import models class Migration(SchemaMigration): def forwards(self, orm): # Adding field 'TreeItem.access_loggedin' db.add_column('sitetree_treeitem', 'access_loggedin', self.gf('django.db.models.fields.BooleanField')(default=False, db_index=True), keep_default=False) def backwards(self, orm): # Deleting field 'TreeItem.access_loggedin' db.delete_column('sitetree_treeitem', 'access_loggedin') models = { 'auth.permission': { 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) }, 'contenttypes.contenttype': { 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) }, 'sitetree.tree': { 'Meta': {'object_name': 'Tree'}, 'alias': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80', 'db_index': 'True'}), 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) }, 'sitetree.treeitem': { 'Meta': {'unique_together': "(('tree', 'alias'),)", 'object_name': 'TreeItem'}, 'access_loggedin': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}), 'access_perm_type': ('django.db.models.fields.IntegerField', [], {'default': '1'}), 'access_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), 'access_restricted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}), 'alias': ('sitetree.models.CharFieldNullable', [], {'db_index': 'True', 'max_length': '80', 'null': 'True', 'blank': 'True'}), 'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}), 'hidden': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}), 'hint': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '200', 'blank': 'True'}), 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 'inbreadcrumbs': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True'}), 'inmenu': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True'}), 'insitetree': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True'}), 'parent': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['sitetree.TreeItem']", 'null': 'True', 'blank': 'True'}), 'sort_order': ('django.db.models.fields.IntegerField', [], {'default': '0', 'db_index': 'True'}), 'title': ('django.db.models.fields.CharField', [], {'max_length': '100'}), 'tree': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['sitetree.Tree']"}), 'url': ('django.db.models.fields.CharField', [], {'max_length': '200', 'db_index': 'True'}), 'urlaspattern': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}) } } complete_apps = ['sitetree'] django-sitetree-1.5.1/sitetree/south_migrations/0004_auto__add_field_tree_title.py000066400000000000000000000103571263651575600303710ustar00rootroot00000000000000# encoding: utf-8 import datetime from south.db import db from south.v2 import SchemaMigration from django.db import models class Migration(SchemaMigration): def forwards(self, orm): # Adding field 'Tree.title' db.add_column('sitetree_tree', 'title', self.gf('django.db.models.fields.CharField')(default='', max_length=100, blank=True), keep_default=False) def backwards(self, orm): # Deleting field 'Tree.title' db.delete_column('sitetree_tree', 'title') models = { 'auth.permission': { 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) }, 'contenttypes.contenttype': { 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) }, 'sitetree.tree': { 'Meta': {'object_name': 'Tree'}, 'alias': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80', 'db_index': 'True'}), 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 'title': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}) }, 'sitetree.treeitem': { 'Meta': {'unique_together': "(('tree', 'alias'),)", 'object_name': 'TreeItem'}, 'access_loggedin': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}), 'access_perm_type': ('django.db.models.fields.IntegerField', [], {'default': '1'}), 'access_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), 'access_restricted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}), 'alias': ('sitetree.models.CharFieldNullable', [], {'db_index': 'True', 'max_length': '80', 'null': 'True', 'blank': 'True'}), 'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}), 'hidden': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}), 'hint': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '200', 'blank': 'True'}), 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 'inbreadcrumbs': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True'}), 'inmenu': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True'}), 'insitetree': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True'}), 'parent': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['sitetree.TreeItem']", 'null': 'True', 'blank': 'True'}), 'sort_order': ('django.db.models.fields.IntegerField', [], {'default': '0', 'db_index': 'True'}), 'title': ('django.db.models.fields.CharField', [], {'max_length': '100'}), 'tree': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['sitetree.Tree']"}), 'url': ('django.db.models.fields.CharField', [], {'max_length': '200', 'db_index': 'True'}), 'urlaspattern': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}) } } complete_apps = ['sitetree'] django-sitetree-1.5.1/sitetree/south_migrations/0005_auto__add_field_treeitem_access_guest.py000066400000000000000000000107601263651575600325760ustar00rootroot00000000000000# -*- coding: utf-8 -*- import datetime from south.db import db from south.v2 import SchemaMigration from django.db import models class Migration(SchemaMigration): def forwards(self, orm): # Adding field 'TreeItem.access_guest' db.add_column('sitetree_treeitem', 'access_guest', self.gf('django.db.models.fields.BooleanField')(default=False, db_index=True), keep_default=False) def backwards(self, orm): # Deleting field 'TreeItem.access_guest' db.delete_column('sitetree_treeitem', 'access_guest') models = { 'auth.permission': { 'Meta': {'object_name': 'Permission', 'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)"}, 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) }, 'contenttypes.contenttype': { 'Meta': {'object_name': 'ContentType', 'db_table': "'django_content_type'", 'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)"}, 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) }, 'sitetree.tree': { 'Meta': {'object_name': 'Tree'}, 'alias': ('django.db.models.fields.CharField', [], {'unique': 'True', 'db_index': 'True', 'max_length': '80'}), 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 'title': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}) }, 'sitetree.treeitem': { 'Meta': {'object_name': 'TreeItem', 'unique_together': "(('tree', 'alias'),)"}, 'access_guest': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}), 'access_loggedin': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}), 'access_perm_type': ('django.db.models.fields.IntegerField', [], {'default': '1'}), 'access_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), 'access_restricted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}), 'alias': ('sitetree.models.CharFieldNullable', [], {'null': 'True', 'blank': 'True', 'db_index': 'True', 'max_length': '80'}), 'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}), 'hidden': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}), 'hint': ('django.db.models.fields.CharField', [], {'max_length': '200', 'default': "''", 'blank': 'True'}), 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 'inbreadcrumbs': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True'}), 'inmenu': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True'}), 'insitetree': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True'}), 'parent': ('django.db.models.fields.related.ForeignKey', [], {'null': 'True', 'related_name': "'treeitem_parent'", 'to': "orm['sitetree.TreeItem']", 'blank': 'True'}), 'sort_order': ('django.db.models.fields.IntegerField', [], {'default': '0', 'db_index': 'True'}), 'title': ('django.db.models.fields.CharField', [], {'max_length': '100'}), 'tree': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'treeitem_tree'", 'to': "orm['sitetree.Tree']"}), 'url': ('django.db.models.fields.CharField', [], {'max_length': '200', 'db_index': 'True'}), 'urlaspattern': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}) } } complete_apps = ['sitetree']django-sitetree-1.5.1/sitetree/south_migrations/__init__.py000066400000000000000000000000001263651575600241430ustar00rootroot00000000000000django-sitetree-1.5.1/sitetree/templates/000077500000000000000000000000001263651575600204445ustar00rootroot00000000000000django-sitetree-1.5.1/sitetree/templates/admin/000077500000000000000000000000001263651575600215345ustar00rootroot00000000000000django-sitetree-1.5.1/sitetree/templates/admin/sitetree/000077500000000000000000000000001263651575600233605ustar00rootroot00000000000000django-sitetree-1.5.1/sitetree/templates/admin/sitetree/tree/000077500000000000000000000000001263651575600243175ustar00rootroot00000000000000django-sitetree-1.5.1/sitetree/templates/admin/sitetree/tree/change_form.html000066400000000000000000000025751263651575600274660ustar00rootroot00000000000000{% extends "admin/change_form.html" %} {% load i18n admin_list sitetree %} {% block after_related_objects %} {% if change %}

 

{% trans "Site Tree Items" %} {{ original.alias }}

 

{% if has_add_permission %} {% endif %}
{% sitetree_tree from original.alias template "admin/sitetree/tree/tree.html" %}
{% trans "Hidden" %} {% trans "Menu" %} {% trans "Breadcrumbs" %} {% trans "Tree" %} {% trans "Restricted" %} {% trans "Users only" %} {% trans "Guests only" %} {% trans "Title" %} {% trans "URL" %} {% trans "Sort order" %}

 

{% endif %} {% endblock %} django-sitetree-1.5.1/sitetree/templates/admin/sitetree/tree/change_list_.html000066400000000000000000000006011263651575600276210ustar00rootroot00000000000000{% extends "admin/change_list.html" %} {% load url from future %} {% load i18n %} {% block object-tools-items %} {% if user.is_superuser %}
  • {% trans "Dump data" %}
  • {% trans "Load data" %}
  • {% endif %} {{ block.super }} {% endblock %} django-sitetree-1.5.1/sitetree/templates/admin/sitetree/tree/tree.html000066400000000000000000000047031263651575600261500ustar00rootroot00000000000000{% load i18n sitetree %} {% load static %} {% get_static_prefix as STATIC_URL %} {% for item in sitetree_items %} {{ item.hidden }} {{ item.inmenu }} {{ item.inbreadcrumbs }} {{ item.insitetree }} {{ item.access_restricted }} {{ item.access_loggedin }} {{ item.access_guest }} {% for d in item.depth_range %}     {% endfor %} {% if item.parent %}|—{% endif %} {% if item.is_dynamic %}{{ item.title }}{% else %}{{ item.title }}{% endif %} {{ item.url }}        {% if item.has_children %} {% sitetree_children of item for sitetree template "admin/sitetree/tree/tree.html" %} {% endif %} {% endfor %} django-sitetree-1.5.1/sitetree/templates/admin/sitetree/tree/tree_combo.html000066400000000000000000000005071263651575600273250ustar00rootroot00000000000000{% load sitetree %}{% for item in sitetree_items %} {{ item.id }}:::{% for d in item.depth_range %}    {% endfor %}{% if item.parent %}|- {% endif %}{{ item.title }} {% if item.has_children %}{% sitetree_children of item for sitetree template "admin/sitetree/tree/tree_combo.html" %}{% endif %}{% endfor %} django-sitetree-1.5.1/sitetree/templates/admin/sitetree/treeitem/000077500000000000000000000000001263651575600251765ustar00rootroot00000000000000django-sitetree-1.5.1/sitetree/templates/admin/sitetree/treeitem/breadcrumbs.html000066400000000000000000000017321263651575600303600ustar00rootroot00000000000000{% load i18n %} {% load sitetree_compat %} django-sitetree-1.5.1/sitetree/templates/admin/sitetree/treeitem/change_form.html000066400000000000000000000037701263651575600303430ustar00rootroot00000000000000{% extends "admin/change_form.html" %} {% load i18n admin_modify %} {% block content_title %}

    {{ title }} {% if not add %}"{{ original.title }}"{% endif %}

    {% endblock %} {% block extrahead %}{{ block.super }} {% endblock %} {% block content %}{{ block.super }} {% endblock %} {% block breadcrumbs %}{% include "admin/sitetree/treeitem/breadcrumbs.html" %}{% endblock %}django-sitetree-1.5.1/sitetree/templates/admin/sitetree/treeitem/delete_confirmation.html000066400000000000000000000002141263651575600320730ustar00rootroot00000000000000{% extends "admin/delete_confirmation.html" %} {% block breadcrumbs %}{% include "admin/sitetree/treeitem/breadcrumbs.html" %}{% endblock %}django-sitetree-1.5.1/sitetree/templates/admin/sitetree/treeitem/object_history.html000066400000000000000000000002071263651575600311120ustar00rootroot00000000000000{% extends "admin/object_history.html" %} {% block breadcrumbs %}{% include "admin/sitetree/treeitem/breadcrumbs.html" %}{% endblock %}django-sitetree-1.5.1/sitetree/templates/sitetree/000077500000000000000000000000001263651575600222705ustar00rootroot00000000000000django-sitetree-1.5.1/sitetree/templates/sitetree/breadcrumbs-title.html000066400000000000000000000002731263651575600265700ustar00rootroot00000000000000{% load sitetree %} {% if sitetree_items|length_is:"1" %} {% else %}{% for item in sitetree_items %}{{ item.title_resolved }}{% if not forloop.last %} - {% endif %}{% endfor %}{% endif %}django-sitetree-1.5.1/sitetree/templates/sitetree/breadcrumbs.html000066400000000000000000000005761263651575600254570ustar00rootroot00000000000000{% load sitetree %} {% if sitetree_items|length_is:"1" %} {% else %}
      {% for item in sitetree_items %} {% if not forloop.last %}
    • {{ item.title_resolved }}
    • >
    • {% else %}
    • {{ item.title_resolved }}
    • {% endif %} {% endfor %}
    {% endif %}django-sitetree-1.5.1/sitetree/templates/sitetree/breadcrumbs_bootstrap.html000066400000000000000000000006431263651575600275470ustar00rootroot00000000000000{% load sitetree %} {% if sitetree_items|length_is:"1" %} {% else %} {% endif %}django-sitetree-1.5.1/sitetree/templates/sitetree/breadcrumbs_bootstrap3.html000066400000000000000000000006041263651575600276270ustar00rootroot00000000000000{% load sitetree %} {% if sitetree_items|length_is:"1" %} {% else %} {% endif %}django-sitetree-1.5.1/sitetree/templates/sitetree/breadcrumbs_foundation.html000066400000000000000000000006261263651575600277010ustar00rootroot00000000000000{% load sitetree %} {% if sitetree_items|length_is:"1" %} {% else %} {% endif %}django-sitetree-1.5.1/sitetree/templates/sitetree/breadcrumbs_semantic.html000066400000000000000000000006671263651575600273430ustar00rootroot00000000000000{% load sitetree %} {% if sitetree_items|length_is:"1" %} {% else %} {% endif %}django-sitetree-1.5.1/sitetree/templates/sitetree/menu.html000066400000000000000000000007671263651575600241340ustar00rootroot00000000000000{% load sitetree %}
      {% for item in sitetree_items %}
    • {{ item.title_resolved }} {% if item.has_children %} {% sitetree_children of item for menu template "sitetree/menu.html" %} {% endif %}
    • {% endfor %}
    django-sitetree-1.5.1/sitetree/templates/sitetree/menu_bootstrap.html000066400000000000000000000013331263651575600262170ustar00rootroot00000000000000{% load sitetree %} django-sitetree-1.5.1/sitetree/templates/sitetree/menu_bootstrap3.html000066400000000000000000000013471263651575600263070ustar00rootroot00000000000000{% load sitetree %} django-sitetree-1.5.1/sitetree/templates/sitetree/menu_bootstrap3_dropdown.html000066400000000000000000000006061263651575600302200ustar00rootroot00000000000000{% load sitetree %} django-sitetree-1.5.1/sitetree/templates/sitetree/menu_bootstrap3_navpills-stacked.html000066400000000000000000000004551263651575600316320ustar00rootroot00000000000000{% load sitetree %} django-sitetree-1.5.1/sitetree/templates/sitetree/menu_bootstrap3_navpills.html000066400000000000000000000004411263651575600302110ustar00rootroot00000000000000{% load sitetree %} django-sitetree-1.5.1/sitetree/templates/sitetree/menu_bootstrap_dropdown.html000066400000000000000000000005261263651575600301360ustar00rootroot00000000000000{% load sitetree %} django-sitetree-1.5.1/sitetree/templates/sitetree/menu_bootstrap_navlist.html000066400000000000000000000004401263651575600277550ustar00rootroot00000000000000{% load sitetree %} django-sitetree-1.5.1/sitetree/templates/sitetree/menu_foundation-vertical.html000066400000000000000000000010641263651575600301600ustar00rootroot00000000000000{% load sitetree %} django-sitetree-1.5.1/sitetree/templates/sitetree/menu_foundation.html000066400000000000000000000010531263651575600263470ustar00rootroot00000000000000{% load sitetree %} django-sitetree-1.5.1/sitetree/templates/sitetree/menu_foundation_flyout.html000066400000000000000000000004671263651575600277610ustar00rootroot00000000000000{% load sitetree %} django-sitetree-1.5.1/sitetree/templates/sitetree/menu_foundation_sidenav.html000066400000000000000000000004041263651575600300570ustar00rootroot00000000000000{% load sitetree %} django-sitetree-1.5.1/sitetree/templates/sitetree/menu_semantic-vertical.html000066400000000000000000000012101263651575600276060ustar00rootroot00000000000000{% load sitetree %} django-sitetree-1.5.1/sitetree/templates/sitetree/menu_semantic.html000066400000000000000000000010621263651575600260040ustar00rootroot00000000000000{% load sitetree %} {% for item in sitetree_items %} {% if item.has_children %} {% else %} {{ item.title_resolved }} {% endif %} {% endfor %}django-sitetree-1.5.1/sitetree/templates/sitetree/menu_semantic_dropdown.html000066400000000000000000000004651263651575600277260ustar00rootroot00000000000000{% load sitetree %} django-sitetree-1.5.1/sitetree/templates/sitetree/tree.html000066400000000000000000000006511263651575600241170ustar00rootroot00000000000000{% load sitetree %} {% if sitetree_items %}
      {% for item in sitetree_items %} {% if item.insitetree %}
    • {{ item.title_resolved }} {% if item.has_children %} {% sitetree_children of item for sitetree template "sitetree/tree.html" %} {% endif %}
    • {% endif %} {% endfor %}
    {% endif %}django-sitetree-1.5.1/sitetree/templatetags/000077500000000000000000000000001263651575600211405ustar00rootroot00000000000000django-sitetree-1.5.1/sitetree/templatetags/__init__.py000066400000000000000000000000001263651575600232370ustar00rootroot00000000000000django-sitetree-1.5.1/sitetree/templatetags/sitetree.py000066400000000000000000000246051263651575600233450ustar00rootroot00000000000000from django import template from django.template.loader import get_template from django.template.base import FilterExpression from ..sitetreeapp import get_sitetree register = template.Library() # All utility methods are implemented in SiteTree class sitetree = get_sitetree() @register.tag def sitetree_tree(parser, token): """Parses sitetree tag parameters. Two notation types are possible: 1. Two arguments: {% sitetree_tree from "mytree" %} Used to render tree for "mytree" site tree. 2. Four arguments: {% sitetree_tree from "mytree" template "sitetree/mytree.html" %} Used to render tree for "mytree" site tree using specific template "sitetree/mytree.html" """ tokens = token.split_contents() use_template = detect_clause(parser, 'template', tokens) tokens_num = len(tokens) if tokens_num in (3, 5): tree_alias = parser.compile_filter(tokens[2]) return sitetree_treeNode(tree_alias, use_template) else: raise template.TemplateSyntaxError( '%r tag requires two arguments. E.g. {%% sitetree_tree from "mytree" %%}.' % tokens[0]) @register.tag def sitetree_children(parser, token): """Parses sitetree_children tag parameters. Six arguments: {% sitetree_children of someitem for menu template "sitetree/mychildren.html" %} Used to render child items of specific site tree 'someitem' using template "sitetree/mychildren.html" for menu navigation. Basically template argument should contain path to current template itself. Allowed navigation types: 1) menu; 2) sitetree. """ tokens = token.split_contents() use_template = detect_clause(parser, 'template', tokens) tokens_num = len(tokens) clauses_in_places = ( tokens_num == 5 and tokens[1] == 'of' and tokens[3] == 'for' and tokens[4] in ('menu', 'sitetree') ) if clauses_in_places and use_template is not None: tree_item = tokens[2] navigation_type = tokens[4] return sitetree_childrenNode(tree_item, navigation_type, use_template) else: raise template.TemplateSyntaxError( '%r tag requires six arguments. ' 'E.g. {%% sitetree_children of someitem for menu template "sitetree/mychildren.html" %%}.' % tokens[0]) @register.tag def sitetree_breadcrumbs(parser, token): """Parses sitetree_breadcrumbs tag parameters. Two notation types are possible: 1. Two arguments: {% sitetree_breadcrumbs from "mytree" %} Used to render breadcrumb path for "mytree" site tree. 2. Four arguments: {% sitetree_breadcrumbs from "mytree" template "sitetree/mycrumb.html" %} Used to render breadcrumb path for "mytree" site tree using specific template "sitetree/mycrumb.html" """ tokens = token.split_contents() use_template = detect_clause(parser, 'template', tokens) tokens_num = len(tokens) if tokens_num == 3: tree_alias = parser.compile_filter(tokens[2]) return sitetree_breadcrumbsNode(tree_alias, use_template) else: raise template.TemplateSyntaxError( '%r tag requires two arguments. E.g. {%% sitetree_breadcrumbs from "mytree" %%}.' % tokens[0]) @register.tag def sitetree_menu(parser, token): """Parses sitetree_menu tag parameters. {% sitetree_menu from "mytree" include "trunk,1,level3" %} Used to render trunk, branch with id 1 and branch aliased 'level3' elements from "mytree" site tree as a menu. These are reserved aliases: * 'trunk' - items without parents * 'this-children' - items under item resolved as current for the current page * 'this-siblings' - items under parent of item resolved as current for the current page (current item included) * 'this-ancestor-children' - items under grandparent item (closest to root) for the item resolved as current for the current page {% sitetree_menu from "mytree" include "trunk,1,level3" template "sitetree/mymenu.html" %} """ tokens = token.split_contents() use_template = detect_clause(parser, 'template', tokens) tokens_num = len(tokens) if tokens_num == 5 and tokens[3] == 'include': tree_alias = parser.compile_filter(tokens[2]) tree_branches = parser.compile_filter(tokens[4]) return sitetree_menuNode(tree_alias, tree_branches, use_template) else: raise template.TemplateSyntaxError( '%r tag requires four arguments. ' 'E.g. {%% sitetree_menu from "mytree" include "trunk,1,level3" %%}.' % tokens[0]) @register.tag def sitetree_url(parser, token): """This tag is much the same as Django built-in 'url' tag. The difference is that after 'for' it should get TreeItem object. """ return sitetree_urlNode.for_tag(parser, token, 'for', 'sitetree_url for someitem') @register.tag def sitetree_page_title(parser, token): """Renders a title for current page, resolved against sitetree item representing current URL.""" return sitetree_page_titleNode.for_tag(parser, token, 'from', 'sitetree_page_title from "mytree"') @register.tag def sitetree_page_description(parser, token): """Renders a description for the current page, resolved against sitetree item representing current URL.""" return sitetree_page_descriptionNode.for_tag(parser, token, 'from', 'sitetree_page_description from "mytree"') @register.tag def sitetree_page_hint(parser, token): """Renders a hint for the current page, resolved against sitetree item representing current URL.""" return sitetree_page_hintNode.for_tag(parser, token, 'from', 'sitetree_page_hint from "mytree"') class sitetree_treeNode(template.Node): """Renders tree items from specified site tree.""" def __init__(self, tree_alias, use_template): self.use_template = use_template self.tree_alias = tree_alias def render(self, context): tree_items = sitetree.tree(self.tree_alias, context) return render(context, tree_items, self.use_template or 'sitetree/tree.html') class sitetree_childrenNode(template.Node): """Renders tree items under specified parent site tree item.""" def __init__(self, tree_item, navigation_type, use_template): self.use_template = use_template self.tree_item = tree_item self.navigation_type = navigation_type def render(self, context): return sitetree.children(self.tree_item, self.navigation_type, self.use_template.resolve(context), context) class sitetree_breadcrumbsNode(template.Node): """Renders breadcrumb trail items from specified site tree.""" def __init__(self, tree_alias, use_template): self.use_template = use_template self.tree_alias = tree_alias def render(self, context): tree_items = sitetree.breadcrumbs(self.tree_alias, context) return render(context, tree_items, self.use_template or 'sitetree/breadcrumbs.html') class sitetree_menuNode(template.Node): """Renders specified site tree menu items.""" def __init__(self, tree_alias, tree_branches, use_template): self.use_template = use_template self.tree_alias = tree_alias self.tree_branches = tree_branches def render(self, context): tree_items = sitetree.menu(self.tree_alias, self.tree_branches, context) return render(context, tree_items, self.use_template or 'sitetree/menu.html') class SimpleNode(template.Node): """Simple node with `as` clause support.""" @classmethod def for_tag(cls, parser, token, preposition, error_hint): """Node constructor to be used in tags.""" tokens = token.split_contents() if len(tokens) >= 3 and tokens[1] == preposition: as_var = cls.get_as_var(tokens) tree_alias = parser.compile_filter(tokens[2]) return cls(tree_alias, as_var) raise template.TemplateSyntaxError( '%r tag requires at least two arguments. E.g. {%% %s %%}.' % (tokens[0], error_hint)) @classmethod def get_as_var(cls, tokens): """Returns context variable from `as` template tag clause if any. Modifies tokens inplace. :param tokens: :rtype: None|str """ as_var = None if tokens[-2] == 'as': as_var = tokens[-1] tokens[-2:] = [] return as_var def __init__(self, item, as_var): self.item = item self.as_var = as_var def get_value(self, context): """Should return a computed value to be used in render().""" def render(self, context): result = self.get_value(context) if self.as_var: context[self.as_var] = result return '' return result class sitetree_urlNode(SimpleNode): """Resolves and renders specified url.""" def get_value(self, context): return sitetree.url(self.item, context) class sitetree_page_titleNode(SimpleNode): """Renders a page title from the specified site tree.""" def get_value(self, context): return sitetree.get_current_page_title(self.item, context) class sitetree_page_descriptionNode(SimpleNode): """Renders a page description from the specified site tree.""" def get_value(self, context): return sitetree.get_current_page_attr('description', self.item, context) class sitetree_page_hintNode(SimpleNode): """Renders a page hint from the specified site tree.""" def get_value(self, context): return sitetree.get_current_page_attr('hint', self.item, context) def detect_clause(parser, clause_name, tokens): """Helper function detects a certain clause in tag tokens list. Returns its value. """ if clause_name in tokens: t_index = tokens.index(clause_name) clause_value = parser.compile_filter(tokens[t_index + 1]) del tokens[t_index:t_index + 2] else: clause_value = None return clause_value def render(context, tree_items, use_template): """Render helper is used by template node functions to render given template with given tree items in context. """ context.push() context['sitetree_items'] = tree_items if isinstance(use_template, FilterExpression): use_template = use_template.resolve(context) content = get_template(use_template).render(context) context.pop() return content django-sitetree-1.5.1/sitetree/templatetags/sitetree_compat.py000066400000000000000000000003101263651575600246730ustar00rootroot00000000000000from django.template import Library register = Library() try: # Django < 1.9.0 from django.templatetags.future import url except ImportError: from django.template.defaulttags import url django-sitetree-1.5.1/sitetree/tests.py000066400000000000000000001063731263651575600201740ustar00rootroot00000000000000import sys from json import loads try: from StringIO import StringIO except ImportError: from io import StringIO try: from unittest import mock except ImportError: import mock from django.conf import settings from django.utils.translation import activate from django.template.base import Template, TemplateSyntaxError from django.template.context import Context from django.test.utils import override_settings from django.test import TestCase from django.contrib.auth.models import Permission from django.contrib.admin.sites import site from django.core.management import call_command from django.core.exceptions import ImproperlyConfigured from django.conf.urls import patterns, url from sitetree.models import Tree, TreeItem from sitetree.forms import TreeItemForm from sitetree.admin import TreeAdmin, TreeItemAdmin, redirects_handler from sitetree.utils import ( tree, item, get_app_n_model, import_app_sitetree_module, import_project_sitetree_modules, get_model_class ) from sitetree.sitetreeapp import ( SiteTree, SiteTreeError, register_items_hook, register_i18n_trees, register_dynamic_trees, compose_dynamic_tree, get_dynamic_trees ) urlpatterns = patterns( '', url(r'articles/', lambda r: None, name='articles_list'), url(r'articles/(\d+)/', lambda r: None, name='articles_detailed'), url(r'articles/(?P\d+)_(?P[\w-]+)/', lambda r: None, name='url'), ) class MockRequest(object): def __init__(self, path=None, user_authorized=None, meta=None): if path is None: path = '/' if user_authorized is None: user_authorized = True self.path = path self.user = MockUser(user_authorized) self.META = meta class MockUser(object): def __init__(self, authorized, permissions=None): self.authorized = authorized self.permissions = permissions or ['auth.add_group', 'perm2'] def is_authenticated(self): return self.authorized def get_all_permissions(self): return self.permissions def get_mock_context(app=None, path=None, user_authorized=False, tree_item=None, put_var=None): ctx = Context( { 'request': MockRequest(path, user_authorized), 't2_root2_title': 'my_real_title', 'art_id': 10, 'tree_item': tree_item, 'somevar_str': 'articles_list', 'somevar_list': ['a', 'b'], 'put_var': put_var }, current_app=app ) ctx.template = mock.MagicMock() ctx.template.engine.string_if_invalid = '' return ctx def render_string(string, context=None, context_put_var=None, context_path=None): return Template(string).render(Context(context or get_mock_context(path=context_path, put_var=context_put_var))) def get_permission_and_name(): perm = Permission.objects.all()[0] perm_name = '%s.%s' % (perm.content_type.app_label, perm.codename) return perm, perm_name class SitetreeTest(TestCase): @classmethod def setUpClass(cls): cls.init_trees() @classmethod def init_trees(cls): cls.sitetree = SiteTree() ########################################################### t1 = Tree(alias='tree1') t1.save() cls.t1 = t1 t1_root = TreeItem(title='root', tree=t1, url='/') t1_root.save() cls.tree_ttags_root = t1_root t1_root_child1 = TreeItem(title='child1', tree=t1, parent=t1_root, url='/about/') t1_root_child1.save() cls.tree_ttags_root_child1 = t1_root_child1 t1_root_child2 = TreeItem(title='child2', tree=t1, parent=t1_root, url='articles_list', urlaspattern=True, description='items_descr') t1_root_child2.save() cls.t1_root_child2 = t1_root_child2 t1_root_child2_sub1 = TreeItem(title='subchild1', tree=t1, parent=t1_root_child2, url='articles_detailed art_id', urlaspattern=True) t1_root_child2_sub1.save() cls.t1_root_child2_sub1 = t1_root_child2_sub1 t1_root_child2_sub2 = TreeItem(title='subchild2', tree=t1, parent=t1_root_child2, url='/not_articles/10/') t1_root_child2_sub2.save() cls.t1_root_child2_sub2 = t1_root_child2_sub2 t1_root_child3 = TreeItem(title='child_with_var_str', tree=t1, parent=t1_root, url='somevar_str', urlaspattern=True) t1_root_child3.save() cls.t1_root_child3 = t1_root_child3 t1_root_child4 = TreeItem(title='child_with_var_list', tree=t1, parent=t1_root, url='somevar_list', urlaspattern=True) t1_root_child4.save() t2 = Tree(alias='tree2') t2.save() cls.t2 = t2 t2_root1 = TreeItem(title='{{ t2_root1_title }}', tree=t2, url='/') t2_root1.save() cls.t2_root1 = t2_root1 t2_root2 = TreeItem(title='put {{ t2_root2_title }} inside', tree=t2, url='/sub/') t2_root2.save() cls.t2_root2 = t2_root2 t2_root3 = TreeItem(title='for logged in only', tree=t2, url='/some/', access_loggedin=True) t2_root3.save() cls.t2_root3 = t2_root3 t2_root4 = TreeItem(title='url quoting', tree=t2, url='url 2 put_var', urlaspattern=True) t2_root4.save() cls.t2_root4 = t2_root4 t2_root5 = TreeItem(title='url quoting 1.5 style', tree=t2, url="'url' 2 put_var", urlaspattern=True) t2_root5.save() cls.t2_root5 = t2_root5 t2_root6 = TreeItem(title='url quoting 1.5 style', tree=t2, url='"url" 2 put_var', urlaspattern=True) t2_root6.save() cls.t2_root6 = t2_root6 t2_root7 = TreeItem(title='for guests only', tree=t2, url='/some_other/', access_guest=True) t2_root7.save() cls.t2_root7 = t2_root7 ########################################################### t3 = Tree(alias='tree3') t3.save() cls.t3 = t3 t3_en_root = TreeItem(title='root', tree=t3, url='/', hidden=True) t3_en_root.save() cls.t3_root = t3_en_root t3_root_child1 = TreeItem(title='child1', tree=t3, parent=t3_en_root, url='/0/', access_loggedin=True) t3_root_child1.save() cls.t3_root_child1 = t3_root_child1 t3_root_child2 = TreeItem(title='child2', tree=t3, parent=t3_en_root, url='/1/', inmenu=True, hidden=True) t3_root_child2.save() cls.t3_root_child2 = t3_root_child2 t3_root_child3 = TreeItem(title='child3', tree=t3, parent=t3_en_root, url='/the_same_url/', inmenu=False) t3_root_child3.save() cls.t3_root_child3 = t3_root_child3 t3_root_child4 = TreeItem(title='child4', tree=t3, parent=t3_en_root, url='/3/', hidden=True) t3_root_child4.save() cls.t3_root_child4 = t3_root_child4 t3_root_child5 = TreeItem(title='child5', tree=t3, parent=t3_en_root, url='/4/', inmenu=True, hidden=True) t3_root_child5.save() cls.t3_root_child5 = t3_root_child5 t3_en = Tree(alias='tree3_en', title='tree3en_title') t3_en.save() cls.t3_en = t3_en t3_en_root = TreeItem(title='root_en', tree=t3_en, url='/') t3_en_root.save() cls.t3_en_root = t3_en_root t3_en_root_child1 = TreeItem(title='child1_en', tree=t3_en, parent=t3_en_root, url='/0_en/') t3_en_root_child1.save() t3_en_root_child2 = TreeItem(title='child2_en', tree=t3_en, parent=t3_en_root, url='/the_same_url/') t3_en_root_child2.save() ########################################################### tree_main = Tree(alias='main') tree_main.save() cls.tree_main = tree_main tree_main_root = TreeItem(title='root', tree=tree_main, url='/', alias='for_dynamic') tree_main_root.save() cls.tree_main_root = tree_main_root @classmethod def tearDownClass(cls): Tree.objects.all().delete() TreeItem.objects.all().delete() class TreeModelTest(SitetreeTest): def test_create_rename_delete(self): tree = Tree(alias='mytree') tree.save() self.assertIsNotNone(tree.id) self.assertEqual(tree.alias, 'mytree') tree.alias = 'not_mytree' tree.save(force_update=True) self.assertEqual(tree.alias, 'not_mytree') tree.delete() self.assertIsNone(tree.id) def test_unique_aliases(self): tree1 = Tree(alias='mytree') tree1.save() tree2 = Tree(alias='mytree') self.assertRaises(Exception, tree2.save) class TreeItemModelTest(SitetreeTest): def test_url_resolve(self): self.sitetree.menu('tree1', 'trunk', get_mock_context(path='/', put_var='abrakadabra')) url = self.sitetree.url(self.t2_root4, get_mock_context(path='/articles/2_slugged/')) self.assertTrue(url.find('abrakadabra') > -1) self.sitetree.menu('tree1', 'trunk', get_mock_context(path='/', put_var='booo')) url = self.sitetree.url(self.t2_root4, get_mock_context(path='/articles/2_slugged-mugged/')) self.assertTrue(url.find('booo') > -1) self.sitetree.menu('tree1', 'trunk', get_mock_context(path='/', put_var='rolling')) url = self.sitetree.url(self.t2_root5, get_mock_context(path='/articles/2_quoted/')) self.assertTrue(url.find('rolling') > -1) self.sitetree.menu('tree1', 'trunk', get_mock_context(path='/', put_var='spoon')) url = self.sitetree.url(self.t2_root6, get_mock_context(path='/articles/2_quoted/')) self.assertTrue(url.find('spoon') > -1) def test_no_tree(self): ti = TreeItem(title='notree_item') self.assertRaises(Exception, ti.save) def test_create_rename_delete(self): ti1 = TreeItem(title='new_root_item', tree=self.t1) ti1.save() self.assertIsNotNone(ti1.id) self.assertEqual(ti1.title, 'new_root_item') ti1.title = 'not_new_root_item' ti1.save(force_update=True) self.assertEqual(ti1.title, 'not_new_root_item') ti1.delete() self.assertIsNone(ti1.id) def test_context_proc_required(self): context = Context() old_debug = settings.DEBUG settings.DEBUG = True self.assertRaises(SiteTreeError, self.sitetree.menu, 'tree1', 'trunk', context) settings.DEBUG = old_debug def test_menu(self): menu = self.sitetree.menu('tree1', 'trunk', get_mock_context(path='/about/')) self.assertEqual(len(menu), 1) self.assertEqual(menu[0].id, self.tree_ttags_root.id) self.assertEqual(menu[0].is_current, False) self.assertEqual(menu[0].depth, 0) self.assertEqual(menu[0].has_children, True) self.assertEqual(menu[0].in_current_branch, True) menu = self.sitetree.menu('tree2', 'trunk', get_mock_context(path='/sub/')) self.assertEqual(len(menu), 6) self.assertEqual(menu[0].id, self.t2_root1.id) self.assertEqual(menu[1].id, self.t2_root2.id) self.assertEqual(menu[0].is_current, False) self.assertEqual(menu[0].in_current_branch, False) self.assertEqual(menu[1].is_current, True) self.assertEqual(menu[1].in_current_branch, True) self.assertEqual(menu[0].depth, 0) self.assertEqual(menu[1].depth, 0) self.assertEqual(menu[0].has_children, False) self.assertEqual(menu[1].has_children, False) def test_breadcrumbs(self): bc1 = self.sitetree.breadcrumbs('tree1', get_mock_context(path='/not_articles/10/')) self.assertEqual(len(bc1), 3) self.assertEqual(bc1[0].id, self.tree_ttags_root.id) self.assertEqual(bc1[1].id, self.t1_root_child2.id) self.assertEqual(bc1[1].url_resolved, '/articles/') self.assertEqual(bc1[2].id, self.t1_root_child2_sub2.id) self.assertEqual(bc1[0].is_current, False) self.assertEqual(bc1[1].is_current, False) self.assertEqual(bc1[2].is_current, True) self.assertEqual(bc1[0].has_children, True) self.assertEqual(bc1[1].has_children, True) self.assertEqual(bc1[2].has_children, False) self.assertEqual(bc1[0].depth, 0) self.assertEqual(bc1[1].depth, 1) self.assertEqual(bc1[2].depth, 2) def test_page_title(self): title = self.sitetree.get_current_page_title('tree1', get_mock_context(path='/articles/')) self.assertEqual(title, self.t1_root_child2.title) title = self.sitetree.get_current_page_title('tree1', get_mock_context(path='/not_articles/')) self.assertEqual(title, '') def test_page_attr(self): attr = self.sitetree.get_current_page_attr('description', 'tree1', get_mock_context(path='/articles/')) self.assertEqual(attr, self.t1_root_child2.description) attr = self.sitetree.get_current_page_attr('description', 'tree1', get_mock_context(path='/not_articles/')) self.assertEqual(attr, '') def test_sitetree(self): st1 = self.sitetree.tree('tree1', get_mock_context(path='/articles/')) self.assertEqual(len(st1), 1) self.assertEqual(st1[0].id, self.tree_ttags_root.id) self.assertEqual(st1[0].is_current, False) self.assertEqual(st1[0].depth, 0) self.assertEqual(st1[0].has_children, True) st2 = self.sitetree.tree('tree2', get_mock_context(path='/')) self.assertIn(self.t2_root7, st2) # Not every item is visible for non logged in. self.assertNotIn(self.t2_root3, st2) self.assertEqual(len(st2), 6) self.assertEqual(st2[0].id, self.t2_root1.id) self.assertEqual(st2[1].id, self.t2_root2.id) self.assertEqual(self.t2_root1.access_loggedin, False) self.assertEqual(self.t2_root1.access_guest, False) self.assertEqual(self.t2_root2.access_loggedin, False) self.assertEqual(self.t2_root2.access_guest, False) self.assertEqual(self.t2_root3.access_loggedin, True) self.assertEqual(self.t2_root3.access_guest, False) self.assertEqual(self.t2_root7.access_loggedin, False) self.assertEqual(self.t2_root7.access_guest, True) self.assertEqual(st2[0].title, '{{ t2_root1_title }}') self.assertEqual(st2[1].title, 'put {{ t2_root2_title }} inside') self.assertEqual(st2[0].title_resolved, '') self.assertEqual(st2[1].title_resolved, 'put my_real_title inside') self.assertEqual(st2[0].is_current, True) self.assertEqual(st2[1].is_current, False) self.assertEqual(st2[0].depth, 0) self.assertEqual(st2[1].depth, 0) self.assertEqual(st2[0].has_children, False) self.assertEqual(st2[1].has_children, False) st2 = self.sitetree.tree('tree2', get_mock_context(path='/', user_authorized=True)) self.assertNotIn(self.t2_root7, st2) # Not every item is visible for non logged in. self.assertIn(self.t2_root3, st2) self.assertEqual(len(st2), 6) def test_items_hook_tree(self): def my_processor(tree_items, tree_sender): for item in tree_items: item.title_resolved = 'FakedTreeItem' return tree_items register_items_hook(my_processor) items = self.sitetree.tree('tree1', get_mock_context(path='/')) register_items_hook(None) self.assertEqual(items[0].title_resolved, 'FakedTreeItem') def test_items_hook_menu(self): def my_processor(tree_items, tree_sender): for item in tree_items: item.title_resolved = 'FakedMenuItem' return tree_items register_items_hook(my_processor) items = self.sitetree.menu('tree1', 'trunk', get_mock_context(path='/')) register_items_hook(None) self.assertEqual(items[0].title_resolved, 'FakedMenuItem') def test_items_hook_breadcrumbs(self): def my_processor(tree_items, tree_sender): for item in tree_items: item.title_resolved = 'FakedBreadcrumbsItem' return tree_items register_items_hook(my_processor) items = self.sitetree.breadcrumbs('tree1', get_mock_context(path='/not_articles/10/')) register_items_hook(None) self.assertEqual(items[0].title_resolved, 'FakedBreadcrumbsItem') class TemplateTagsTest(SitetreeTest): @classmethod def setUpClass(cls): cls.sitetree = SiteTree() tree_ttags = Tree(alias='ttags') tree_ttags.save() cls.tree_ttags = tree_ttags tree_ttags_root = TreeItem( title='root', tree=tree_ttags, url='/', insitetree=True, inbreadcrumbs=True, inmenu=True, hint='roothint', description='rootdescr' ) tree_ttags_root.save() cls.tree_ttags_root = tree_ttags_root tree_ttags_root_child1 = TreeItem( title='sometitle', tree=tree_ttags, parent=tree_ttags_root, url='/child1', insitetree=True, inbreadcrumbs=True, inmenu=True, hint='somehint', description='somedescr' ) tree_ttags_root_child1.save() cls.tree_ttags_root_child1 = tree_ttags_root_child1 def test_sitetree_tree(self): tpl = '{% load sitetree %}{% sitetree_tree "mytree" %}' self.assertRaises(TemplateSyntaxError, render_string, tpl) tpl = '{% load sitetree %}{% sitetree_tree from "mytree" %}' result = render_string(tpl) self.assertEqual(result.strip(), '') tpl = '{% load sitetree %}{% sitetree_tree from "ttags" %}' result = render_string(tpl) self.assertIn('href="/"', result) def test_sitetree_children(self): context = get_mock_context(put_var=self.tree_ttags_root) self.sitetree.set_global_context(context) tpl = '{% load sitetree %}{% sitetree_children %}' self.assertRaises(TemplateSyntaxError, render_string, tpl) tpl = '{% load sitetree %}{% sitetree_children of put_var for sitetree template "sitetree/tree.html" %}' result = render_string(tpl, context=context) self.assertIn('href="/child1"', result) def test_sitetree_breadcrumbs(self): tpl = '{% load sitetree %}{% sitetree_breadcrumbs %}' self.assertRaises(TemplateSyntaxError, render_string, tpl) tpl = '{% load sitetree %}{% sitetree_breadcrumbs from "mytree" %}' result = render_string(tpl) self.assertEqual(result.strip(), '
      \n\t\n
    ') tpl = '{% load sitetree %}{% sitetree_breadcrumbs from "ttags" %}' result = render_string(tpl, context_path='/child1') self.assertIn('href="/"', result) self.assertIn('root', result) self.assertIn('sometitle', result) def test_sitetree_menu(self): tpl = '{% load sitetree %}{% sitetree_menu %}' self.assertRaises(TemplateSyntaxError, render_string, tpl) tpl = '{% load sitetree %}{% sitetree_menu from "mytree" include "trunk" %}' result = render_string(tpl) self.assertEqual(result.strip(), '
      \n\t\n
    ') tpl = '{% load sitetree %}{% sitetree_menu from "ttags" include "trunk" %}' result = render_string(tpl, context_path='/child1') self.assertIn('current_branch">root', result) self.assertIn('current_item current_branch">sometitle', result) def test_sitetree_page_title(self): tpl = '{% load sitetree %}{% sitetree_page_title %}' self.assertRaises(TemplateSyntaxError, render_string, tpl) with override_settings(DEBUG=True): tpl = '{% load sitetree %}{% sitetree_page_title from "ttags" %}' self.assertRaises(SiteTreeError, render_string, tpl, context_path='/somewhere') tpl = '{% load sitetree %}{% sitetree_page_title from "ttags" %}' result = render_string(tpl, context_path='/child1') self.assertEqual(result, 'sometitle') context = get_mock_context() tpl = '{% load sitetree %}{% sitetree_page_title from "ttags" as somev %}' render_string(tpl, context=context) self.assertEqual(context.get('somev'), 'root') def test_sitetree_page_hint(self): tpl = '{% load sitetree %}{% sitetree_page_hint %}' self.assertRaises(TemplateSyntaxError, render_string, tpl) with override_settings(DEBUG=True): tpl = '{% load sitetree %}{% sitetree_page_hint from "ttags" %}' self.assertRaises(SiteTreeError, render_string, tpl, context_path='/somewhere') tpl = '{% load sitetree %}{% sitetree_page_hint from "ttags" %}' result = render_string(tpl, context_path='/child1') self.assertEqual(result, 'somehint') context = get_mock_context() tpl = '{% load sitetree %}{% sitetree_page_hint from "ttags" as somev %}' render_string(tpl, context=context) self.assertEqual(context.get('somev'), 'roothint') def test_sitetree_page_description(self): tpl = '{% load sitetree %}{% sitetree_page_description %}' self.assertRaises(TemplateSyntaxError, render_string, tpl) with override_settings(DEBUG=True): tpl = '{% load sitetree %}{% sitetree_page_description from "ttags" %}' self.assertRaises(SiteTreeError, render_string, tpl, context_path='/somewhere') tpl = '{% load sitetree %}{% sitetree_page_description from "ttags" %}' result = render_string(tpl, context_path='/child1') self.assertEqual(result, 'somedescr') context = get_mock_context() tpl = '{% load sitetree %}{% sitetree_page_description from "ttags" as somev %}' render_string(tpl, context=context) self.assertEqual(context.get('somev'), 'rootdescr') def test_sitetree_url(self): tpl = '{% load sitetree %}{% sitetree_url %}' self.assertRaises(TemplateSyntaxError, render_string, tpl) context = get_mock_context(path='/child1', put_var=self.tree_ttags_root_child1) tpl = '{% load sitetree %}{% sitetree_url for put_var %}' result = render_string(tpl, context) self.assertEqual(result, '/child1') tpl = '{% load sitetree %}{% sitetree_url for put_var as res_var %}' render_string(tpl, context) self.assertEqual(context.get('res_var'), '/child1') class TreeTest(SitetreeTest): def test_str(self): self.assertEqual(self.t3.alias, str(self.t3)) def test_get_title(self): self.assertEqual(self.t3.get_title(), 'tree3') self.assertEqual(self.t3_en.get_title(), 'tree3en_title') def test_children_filtering(self): self.sitetree._global_context = get_mock_context(path='/') self.sitetree.get_sitetree('tree3') children = self.sitetree.get_children('tree3', self.t3_root) filtered = self.sitetree.filter_items(children, 'menu') self.assertEqual(filtered, []) def test_tree_filtering(self): tree = self.sitetree.tree('tree3', get_mock_context(path='/')) self.assertEqual(len(tree), 0) def test_register_i18n_trees(self): register_i18n_trees(['tree3']) self.sitetree._global_context = get_mock_context(path='/the_same_url/') activate('en') self.sitetree.get_sitetree('tree3') children = self.sitetree.get_children('tree3', self.t3_en_root) self.assertEqual(len(children), 2) self.assertFalse(children[0].is_current) self.assertTrue(children[1].is_current) activate('ru') self.sitetree.lang_init() self.sitetree.get_sitetree('tree3') children = self.sitetree.get_children('tree3', self.t3_root) self.assertEqual(len(children), 5) self.assertFalse(children[1].is_current) self.assertTrue(children[2].is_current) self.assertFalse(children[3].is_current) class DynamicTreeTest(SitetreeTest): def test_basic_old_and_new(self): # Assert no dynamic attached. tree_alias, sitetree_items = self.sitetree.get_sitetree('main') self.assertEqual(len(sitetree_items), 1) # Assert cache hit. tree_alias, sitetree_items = self.sitetree.get_sitetree('main') self.assertEqual(len(sitetree_items), 1) # Empty cache before dynamic items added. self.sitetree.cache.empty() self.assertEqual(len(get_dynamic_trees().keys()), 0) self.basic_test() # old-style self.assertEqual(len(get_dynamic_trees().keys()), 3) self.basic_test(new_style=True) self.basic_test(new_style=True, reset_cache=True) self.assertEqual(len(get_dynamic_trees().keys()), 3) def basic_test(self, new_style=False, reset_cache=False): trees = ( compose_dynamic_tree(( tree('dynamic_main_root', items=( item('dynamic_main_root_1', 'dynamic_main_root_1_url', url_as_pattern=False, sort_order=2), item('dynamic_main_root_2', 'dynamic_main_root_2_url', url_as_pattern=False, sort_order=1), )), ), target_tree_alias='main'), compose_dynamic_tree(( tree('dynamic_main_sub', items=( item('dynamic_main_sub_1', 'dynamic_main_sub_1_url', url_as_pattern=False, access_by_perms=['auth.add_group', 'auth.change_group']), item('dynamic_main_sub_2', 'dynamic_main_sub_2_url', url_as_pattern=False, access_by_perms=['auth.add_group'], perms_mode_all=False), )), ), target_tree_alias='main', parent_tree_item_alias='for_dynamic'), compose_dynamic_tree(( tree('dynamic', items=( item('dynamic_1', 'dynamic_1_url', children=( item('dynamic_1_sub_1', 'dynamic_1_sub_1_url', url_as_pattern=False), ), url_as_pattern=False), item('dynamic_2', 'dynamic_2_url', url_as_pattern=False), )), )), ) kwargs = { 'reset_cache': reset_cache } if new_style: register_dynamic_trees(*trees, **kwargs) else: register_dynamic_trees(trees, **kwargs) mock_context = get_mock_context(path='/the_same_url/') self.sitetree._global_context = mock_context tree_alias, sitetree_items = self.sitetree.get_sitetree('main') if reset_cache: self.assertEqual(len(sitetree_items), 13) children = self.sitetree.get_children('main', self.tree_main_root) self.assertEqual(len(children), 6) tree_alias, sitetree_items = self.sitetree.get_sitetree('dynamic') self.assertEqual(len(sitetree_items), 9) children = self.sitetree.get_children('dynamic', sitetree_items[0]) self.assertEqual(len(children), 1) else: mock_user = MockUser(True) self.assertFalse(self.sitetree.check_access(sitetree_items[1], {'user': mock_user})) self.assertTrue(self.sitetree.check_access(sitetree_items[2], {'user': mock_user})) self.assertFalse(self.sitetree.check_access(sitetree_items[2], { 'user': MockUser(True, permissions=['dummy.dummy'])})) self.assertEqual(len(sitetree_items), 5) self.assertEqual(sitetree_items[1].perms, set(['auth.add_group', 'auth.change_group'])) self.assertEqual(sitetree_items[3].title, 'dynamic_main_root_1') self.assertEqual(sitetree_items[4].title, 'dynamic_main_root_2') self.assertEqual(sitetree_items[3].sort_order, 2) self.assertEqual(sitetree_items[4].sort_order, 1) self.assertIsNone(getattr(sitetree_items[3], 'perms', None)) children = self.sitetree.get_children('main', self.tree_main_root) self.assertEqual(len(children), 2) tree_alias, sitetree_items = self.sitetree.get_sitetree('dynamic') self.assertEqual(len(sitetree_items), 3) children = self.sitetree.get_children('dynamic', sitetree_items[0]) self.assertEqual(len(children), 1) class UtilsItemTest(SitetreeTest): def test_permission_any(self): i1 = item('root', 'url') self.assertEqual(i1.access_perm_type, i1.PERM_TYPE_ALL) i2 = item('root', 'url', perms_mode_all=True) self.assertEqual(i2.access_perm_type, i1.PERM_TYPE_ALL) i3 = item('root', 'url', perms_mode_all=False) self.assertEqual(i3.access_perm_type, i1.PERM_TYPE_ANY) def test_permissions_none(self): i1 = item('root', 'url') self.assertEqual(i1.permissions, []) def test_int_permissions(self): i1 = item('root', 'url', access_by_perms=[1, 2, 3]) self.assertEqual(i1.permissions, [1, 2, 3]) def test_import_project_sitetree_modules(self): cls = get_model_class('MODEL_TREE') self.assertIs(cls, Tree) def test_get_model_class(self): import_project_sitetree_modules() def test_import_app_sitetree_module(self): self.assertRaises(ImportError, import_app_sitetree_module, 'sitetre') def test_get_app_n_model(self): app, model = get_app_n_model('MODEL_TREE') self.assertEqual(app, 'sitetree') self.assertEqual(model, 'Tree') self.assertRaises(ImproperlyConfigured, get_app_n_model, 'ALIAS_TRUNK') def test_valid_string_permissions(self): perm, perm_name = get_permission_and_name() i1 = item('root', 'url', access_by_perms=perm_name) self.assertEqual(i1.permissions, [perm]) def test_perm_obj_permissions(self): perm, __ = get_permission_and_name() i1 = item('root', 'url', access_by_perms=perm) self.assertEqual(i1.permissions, [perm]) def test_bad_string_permissions(self): self.assertRaises(ValueError, item, 'root', 'url', access_by_perms='bad name') self.assertRaises(ValueError, item, 'root', 'url', access_by_perms='unknown.name') self.assertRaises(ValueError, item, 'root', 'url', access_by_perms=42.2) def test_access_restricted(self): # Test that default is False i0 = item('root', 'url', access_by_perms=1) self.assertEqual(i0.access_restricted, True) # True is respected i1 = item('root', 'url') self.assertEqual(i1.access_restricted, False) class TestAdmin(SitetreeTest): def test_redirects_handler(self): def get_handler(referer, item_id=None): req = MockRequest(referer, True, { 'HTTP_REFERER': referer }) args = [req] kwargs = {} if item_id is not None: kwargs['item_id'] = item_id return redirects_handler(*args, **kwargs) handler = get_handler('/') self.assertEqual(handler._headers['location'][1], '/../') handler = get_handler('/delete/') self.assertEqual(handler._headers['location'][1], '/delete/../../') handler = get_handler('/history/') self.assertEqual(handler._headers['location'][1], '/history/../../') handler = get_handler('/history/', 42) self.assertEqual(handler._headers['location'][1], '/history/../') def test_tree_item_admin(self): admin = TreeItemAdmin(TreeItem, site) admin.tree = Tree.objects.get(alias='main') form = admin.get_form(MockRequest()) self.assertEqual(len(form.known_url_names), 3) self.assertIn('articles_list', form.known_url_names) self.assertIn('articles_detailed', form.known_url_names) self.assertIn('url', form.known_url_names) def test_tree_item_admin_get_tree(self): main_tree = Tree.objects.get(alias='main') main_tree_item = TreeItem.objects.filter(tree__alias='main')[0] admin = TreeItemAdmin(TreeItem, site) tree = admin.get_tree(MockRequest(), main_tree.pk) self.assertEqual(tree.alias, 'main') tree = admin.get_tree(MockRequest(), None, main_tree_item.pk) self.assertEqual(tree.alias, 'main') def test_tree_item_admin_item_move(self): main_tree = Tree.objects.get(alias='main') admin = TreeItemAdmin(TreeItem, site) new_item_1 = TreeItem(title='title_1', sort_order=1, tree_id=main_tree.pk) new_item_1.save() new_item_2 = TreeItem(title='title_2', sort_order=2, tree_id=main_tree.pk) new_item_2.save() new_item_3 = TreeItem(title='title_3', sort_order=3, tree_id=main_tree.pk) new_item_3.save() admin.item_move(None, None, new_item_2.id, 'up') self.assertEqual(TreeItem.objects.get(pk=new_item_1.id).sort_order, 2) self.assertEqual(TreeItem.objects.get(pk=new_item_2.id).sort_order, 1) self.assertEqual(TreeItem.objects.get(pk=new_item_3.id).sort_order, 3) admin.item_move(None, None, new_item_1.id, 'down') self.assertEqual(TreeItem.objects.get(pk=new_item_1.id).sort_order, 3) self.assertEqual(TreeItem.objects.get(pk=new_item_2.id).sort_order, 1) self.assertEqual(TreeItem.objects.get(pk=new_item_3.id).sort_order, 2) def test_tree_item_admin_save_model(self): main_tree = Tree.objects.get(alias='main') tree_item = TreeItem.objects.filter(tree__alias='main')[0] admin = TreeItemAdmin(TreeItem, site) admin.tree = main_tree admin.save_model(MockRequest(), tree_item, None, change=True) self.assertIs(tree_item.tree, admin.tree) def test_tree_admin(self): admin = TreeAdmin(Tree, site) urls = admin.get_urls() self.assertIn('tree_id', urls[1]._regex) class TestForms(SitetreeTest): def test_basic(self): form = TreeItemForm(tree='main', tree_item='root') self.assertIn('tree_item', form.fields) self.assertEqual(form.fields['tree_item'].tree, 'main') self.assertEqual(form.fields['tree_item'].initial, 'root') self.assertEqual(form.fields['tree_item'].choices[1][1], 'root') class TestManagementCommands(SitetreeTest): def setUp(self): self.file_contents = ( '[{"pk": 2, "fields": {"alias": "/tree1/", "title": "tree one"}, "model": "sitetree.tree"}, ' '{"pk": 3, "fields": {"alias": "/tree2/", "title": "tree two"}, "model": "sitetree.tree"}, ' '{"pk": 7, "fields": {"access_restricted": false, "inmenu": true, "title": "tree item one",' ' "hidden": false, "description": "", "alias": null, "url": "/tree1/item1/", "access_loggedin": false,' ' "urlaspattern": false, "access_perm_type": 1, "tree": 2, "hint": "", "inbreadcrumbs": true,' ' "access_permissions": [], "sort_order": 7, "access_guest": false, "parent": null, "insitetree": true},' ' "model": "sitetree.treeitem"}]') def test_sitetreedump(self): stdout = sys.stdout sys.stdout = StringIO() call_command('sitetreedump') output = loads(sys.stdout.getvalue()) sys.stdout = stdout self.assertEqual(output[0]['model'], 'sitetree.tree') self.assertEqual(output[5]['model'], 'sitetree.treeitem') def test_sitetreeload(self): try: import __builtin__ patch_val = '__builtin__.open' except ImportError: # python3 patch_val = 'builtins.open' with mock.patch(patch_val) as mock_file: mock_file.return_value.__enter__ = lambda s: s mock_file.return_value.__exit__ = mock.Mock() mock_file.return_value.read.return_value = self.file_contents call_command('sitetreeload', 'somefile.json') self.assertTrue(Tree.objects.filter(title='tree one').exists()) self.assertTrue(Tree.objects.filter(title='tree two').exists()) self.assertTrue(TreeItem.objects.filter(title='tree item one').exists()) django-sitetree-1.5.1/sitetree/utils.py000066400000000000000000000151021263651575600201570ustar00rootroot00000000000000from django.contrib.auth.models import Permission from django.core.exceptions import ImproperlyConfigured from django.utils import six from django.utils.module_loading import module_has_submodule try: from importlib import import_module except ImportError: # Python < 2.7 from django.utils.importlib import import_module try: from django.apps import apps apps_get_model = apps.get_model except ImportError: # Django < 1.7 from django.db.models import get_model apps_get_model = None from sitetree import settings def generate_id_for(obj): """Generates and returns a unique identifier for the given object.""" return id(obj) def tree(alias, title='', items=None): """Dynamically creates and returns a sitetree. `items` - dynamic sitetree items objects created by `item` function. """ tree_obj = get_tree_model()(alias=alias, title=title) tree_obj.id = generate_id_for(tree_obj) tree_obj.is_dynamic = True if items is not None: tree_obj.dynamic_items = [] def traverse(items): for item in items: item.tree = tree_obj tree_obj.dynamic_items.append(item) if hasattr(item, 'dynamic_children'): traverse(item.dynamic_children) traverse(items) return tree_obj def item(title, url, children=None, url_as_pattern=True, hint='', alias='', description='', in_menu=True, in_breadcrumbs=True, in_sitetree=True, access_loggedin=False, access_guest=False, access_by_perms=None, perms_mode_all=True, **kwargs): """Dynamically creates and returns a sitetree item object. :param str title: :param str url: :param list, set children: a list of children for tree item. Children should also be created by `item` function. :param bool url_as_pattern: consider URL as a name of a named URL :param str hint: hints are usually shown to users :param str alias: item name to address it from templates :param str description: additional information on item (usually is not shown to users) :param bool in_menu: show this item in menus :param bool in_breadcrumbs: show this item in breadcrumbs :param bool in_sitetree: show this item in sitetrees :param bool access_loggedin: show item to logged in users only :param bool access_guest: show item to guest users only :param list, str, int, Permission access_by_perms: restrict access to users with these permissions :param bool perms_mode_all: permissions set interpretation rule: True - user should have all the permissions; False - user should have any of chosen permissions. :return: """ item_obj = get_tree_item_model()(title=title, url=url, urlaspattern=url_as_pattern, hint=hint, alias=alias, description=description, inmenu=in_menu, insitetree=in_sitetree, inbreadcrumbs=in_breadcrumbs, access_loggedin=access_loggedin, access_guest=access_guest, **kwargs) item_obj.id = generate_id_for(item_obj) item_obj.is_dynamic = True item_obj.dynamic_children = [] cleaned_permissions = [] if access_by_perms: # Make permissions a list if currently a single object if not isinstance(access_by_perms, list): access_by_perms = [access_by_perms] for perm in access_by_perms: if isinstance(perm, six.string_types): # Get permission object from string try: app, codename = perm.split('.') except ValueError: raise ValueError( 'Wrong permission string format: supplied - `%s`; ' 'expected - `.`.' % perm) try: perm = Permission.objects.get(codename=codename, content_type__app_label=app) except Permission.DoesNotExist: raise ValueError('Permission `%s.%s` does not exist.' % (app, codename)) elif not isinstance(perm, (int, Permission)): raise ValueError('Permissions must be given as strings, ints, or `Permission` instances.') cleaned_permissions.append(perm) item_obj.permissions = cleaned_permissions or [] item_obj.access_perm_type = item_obj.PERM_TYPE_ALL if perms_mode_all else item_obj.PERM_TYPE_ANY if item_obj.permissions: item_obj.access_restricted = True if children is not None: for child in children: child.parent = item_obj item_obj.dynamic_children.append(child) return item_obj def import_app_sitetree_module(app): """Imports sitetree module from a given app.""" module_name = settings.APP_MODULE_NAME module = import_module(app) try: sub_module = import_module('%s.%s' % (app, module_name)) return sub_module except ImportError: if module_has_submodule(module, module_name): raise return None def import_project_sitetree_modules(): """Imports sitetrees modules from packages (apps).""" from django.conf import settings as django_settings submodules = [] for app in django_settings.INSTALLED_APPS: module = import_app_sitetree_module(app) if module is not None: submodules.append(module) return submodules def get_app_n_model(settings_entry_name): """Returns tuple with application and tree[item] model class names.""" try: app_name, model_name = getattr(settings, settings_entry_name).split('.') except ValueError: raise ImproperlyConfigured('`SITETREE_%s` must have the following format: `app_name.model_name`.' % settings_entry_name) return app_name, model_name def get_model_class(settings_entry_name): """Returns a certain sitetree model as defined in the project settings.""" app_name, model_name = get_app_n_model(settings_entry_name) if apps_get_model is None: model = get_model(app_name, model_name) else: try: model = apps_get_model(app_name, model_name) except (LookupError, ValueError): model = None if model is None: raise ImproperlyConfigured('`SITETREE_%s` refers to model `%s` that has not been installed.' % (settings_entry_name, model_name)) return model def get_tree_model(): """Returns the Tree model, set for the project.""" return get_model_class('MODEL_TREE') def get_tree_item_model(): """Returns the TreeItem model, set for the project.""" return get_model_class('MODEL_TREE_ITEM') django-sitetree-1.5.1/tox.ini000066400000000000000000000024661263651575600161450ustar00rootroot00000000000000[tox] envlist = py27-django14, py27-django15, py27-django16, py27-django17, py27-django18, py27-django19, py3-django15, py3-django16, py3-django17, py3-django18, py3-django19 [django14] deps = Django>=1.4,<1.5 [django15] deps = Django>=1.5,<1.6 [django16] deps = Django>=1.6,<1.7 [django17] deps = Django>=1.7,<1.8 [django18] deps = Django>=1.8,<1.9 [django19] deps = Django==1.9.0 [testenv] commands = python sitetree/runtests.py [testenv:py27-django14] basepython = python2.7 deps = {[django14]deps} mock [testenv:py27-django15] basepython = python2.7 deps = {[django15]deps} mock [testenv:py27-django16] basepython = python2.7 deps = {[django16]deps} mock [testenv:py27-django17] basepython = python2.7 deps = {[django17]deps} mock [testenv:py27-django18] basepython = python2.7 deps = {[django18]deps} mock [testenv:py27-django19] basepython = python2.7 deps = {[django19]deps} mock [testenv:py3-django15] basepython = python3 deps = {[django15]deps} [testenv:py3-django16] basepython = python3 deps = {[django16]deps} [testenv:py3-django17] basepython = python3 deps = {[django17]deps} [testenv:py3-django18] basepython = python3 deps = {[django18]deps} [testenv:py3-django19] basepython = python3 deps = {[django19]deps}