django-sitetree-1.9.0/000077500000000000000000000000001321511234300146005ustar00rootroot00000000000000django-sitetree-1.9.0/.codeclimate.yml000066400000000000000000000003061321511234300176510ustar00rootroot00000000000000languages: JavaScript: true Python: true exclude_paths: - "sitetree/runtests.py" - "sitetree/tests.py" - "sitetree/tests/*" - "sitetree/migrations/*" - "sitetree/south_migrations/*" - "docs/*" django-sitetree-1.9.0/.coveragerc000066400000000000000000000002211321511234300167140ustar00rootroot00000000000000[run] include = sitetree/* omit = sitetree/migrations/*, sitetree/south_migrations/*, sitetree/tests/*, sitetree/runtests.py, sitetree/config.py django-sitetree-1.9.0/.gitignore000066400000000000000000000001141321511234300165640ustar00rootroot00000000000000*.pyc .project .pydevproject .idea .tox django_sitetree.egg-info docs/build django-sitetree-1.9.0/.landscape.yaml000066400000000000000000000002101321511234300174650ustar00rootroot00000000000000strictness: medium uses: - django autodetect: yes ignore-paths: - docs - sitetree/migrations - sitetree/south_migrationsdjango-sitetree-1.9.0/.travis.yml000066400000000000000000000010771321511234300167160ustar00rootroot00000000000000language: python python: - 3.6 - 3.5 - 3.4 - 2.7 env: - DJANGO="Django>=2.0,<2.1" - DJANGO="Django>=1.11,<1.12" - DJANGO="Django>=1.10,<1.11" - DJANGO="Django>=1.9,<1.10" - DJANGO="Django>=1.8.6,<1.9" - DJANGO="Django>=1.7,<1.8" install: - pip install -U coverage coveralls $DJANGO script: coverage run -a --source=sitetree setup.py test matrix: exclude: - python: 3.6 env: DJANGO="Django>=1.7,<1.8" - python: 3.5 env: DJANGO="Django>=1.7,<1.8" - python: 2.7 env: DJANGO="Django>=2.0,<2.1" after_success: coveralls django-sitetree-1.9.0/AUTHORS000066400000000000000000000040331321511234300156500ustar00rootroot00000000000000django-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 Sergey Maranchuk Martey Dodoo Michał Suszko Piter Vergara Chris Lamb stop5 Translators ----------- Russian: Igor Starikov Ukranian: Sergiy Gavrylov German: Danilo Bargen Persian: Ali Javadi Spanish: Adrián López Calvo Norwegian: Eirik Krogstad French: Jean Traullé django-sitetree-1.9.0/CHANGELOG000066400000000000000000000212441321511234300160150ustar00rootroot00000000000000django-sitetree changelog ========================= v1.9.0 ------ + Added Bootstrap 4 templates. * Dropped support for Python 2.6 and 3.3. * Dropped Django<1.7 related code. + Added basic Django 2.0 compatibility. + Added French translation. * Reduced number of SQL queries on item admin page (see #237) v1.8.0 ------ + IMPORTANT: i18n trees now support full lang names (e.g. de-ch, pt-br), update your i18n trees aliases. + Django 1.11 compatibility improvements. v1.7.0 ------ * IMPORTANT: Caching reworked. * IMPORTANT: Dropped Django 1.5, 1.6 support (will not be tested anymore). + Added `ITEMS_FIELD_ROOT_ID` setting (see #205). * Reduced DB hits for trees with lots of permissions (see #213). * Improved `sitetreeload` command py3 compatibility (see #209). * Fixed `sitetreeload` unable to load some twisted tree structures (see #209). * Fixed `sitetree_resync_apps` run without args. * Fixed package distribution (see #222). v1.6.0 ------ + Prevent TreeItems from being their own parents (see #200). + Added `toolbox` module as API single entry point. + Added `exceptions` module. * Django 1.10 compatibility improvements. * Cache.reset() misbehavior fixed (closes #191). * Fixed broken delete operation in admin for custom TreeItem (see #190). * Reduced number of cache calls (see #194). 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.9.0/INSTALL000066400000000000000000000007631321511234300156370ustar00rootroot00000000000000django-sitetree installation ============================ Python ``pip`` package is required to install ``django-sitetree``. From sources ------------ Use the following command line to install ``django-sitetree`` from sources directory (containing setup.py): pip install . or python setup.py install From PyPI --------- Alternatively you can install ``django-sitetree`` from PyPI: pip install django-sitetree Use `-U` flag for upgrade: pip install -U django-sitetree django-sitetree-1.9.0/LICENSE000066400000000000000000000030171321511234300156060ustar00rootroot00000000000000Copyright (c) 2010-2017, 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 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.9.0/MANIFEST.in000066400000000000000000000006311321511234300163360ustar00rootroot00000000000000include 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.9.0/README.rst000066400000000000000000000044251321511234300162740ustar00rootroot00000000000000django-sitetree =============== http://github.com/idlesign/django-sitetree |release| |lic| |ci| |coverage| |health| .. |release| image:: https://img.shields.io/pypi/v/django-sitetree.svg :target: https://pypi.python.org/pypi/django-sitetree .. |lic| image:: https://img.shields.io/pypi/l/django-sitetree.svg :target: https://pypi.python.org/pypi/django-sitetree .. |ci| image:: https://img.shields.io/travis/idlesign/django-sitetree/master.svg :target: https://travis-ci.org/idlesign/django-sitetree .. |coverage| image:: https://img.shields.io/coveralls/idlesign/django-sitetree/master.svg :target: https://coveralls.io/r/idlesign/django-sitetree .. |health| 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.9.0/docs/000077500000000000000000000000001321511234300155305ustar00rootroot00000000000000django-sitetree-1.9.0/docs/Makefile000066400000000000000000000110261321511234300171700ustar00rootroot00000000000000# 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.9.0/docs/build/000077500000000000000000000000001321511234300166275ustar00rootroot00000000000000django-sitetree-1.9.0/docs/build/.gitignore000066400000000000000000000000001321511234300206050ustar00rootroot00000000000000django-sitetree-1.9.0/docs/source/000077500000000000000000000000001321511234300170305ustar00rootroot00000000000000django-sitetree-1.9.0/docs/source/addons.rst000066400000000000000000000005431321511234300210340ustar00rootroot00000000000000Thirdparty 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.9.0/docs/source/admin.rst000066400000000000000000000045131321511234300206550ustar00rootroot00000000000000Overriding 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.9.0/docs/source/apps.rst000066400000000000000000000064001321511234300205250ustar00rootroot00000000000000Shipping 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.9.0/docs/source/conf.py000066400000000000000000000160001321511234300203240ustar00rootroot00000000000000# -*- 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-2017, 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.9.0/docs/source/forms.rst000066400000000000000000000031731321511234300207140ustar00rootroot00000000000000SiteTree 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.9.0/docs/source/hooks.rst000066400000000000000000000034101321511234300207030ustar00rootroot00000000000000Tree 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.9.0/docs/source/i18n.rst000066400000000000000000000031301321511234300203360ustar00rootroot00000000000000Internationalization ==================== 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`, `my_tree_pt-br`. # 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/ # for more information. django-sitetree-1.9.0/docs/source/index.rst000066400000000000000000000054011321511234300206710ustar00rootroot00000000000000django-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.7+, 3.4+ 2. Django 1.7+ 3. Auth Django contrib package 4. Admin site Django contrib package (optional) 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.9.0/docs/source/management.rst000066400000000000000000000050431321511234300217000ustar00rootroot00000000000000Management 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.9.0/docs/source/models.rst000066400000000000000000000073511321511234300210530ustar00rootroot00000000000000SiteTree 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.9.0/docs/source/performance.rst000066400000000000000000000023051321511234300220630ustar00rootroot00000000000000Performance 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.9.0/docs/source/quickstart.rst000066400000000000000000000100501321511234300217500ustar00rootroot00000000000000Getting 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 migrate`` to install sitetree tables into database. 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.9.0/docs/source/tags.rst000066400000000000000000000123051321511234300205210ustar00rootroot00000000000000SiteTree 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-parent-siblings** - items under parent item for the item resolved as current for the current page. Considering that we are now at `Public` renders:: Web Postal + **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 all items under `Home` (which is closest to the root). 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-page-hint: sitetree_page_hint ------------------ This tag is similar to `sitetree_page_description`, but it uses data from tree item `hint` field instead of a `description` fields. Usage example:: {% sitetree_page_hint from "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.9.0/docs/source/tagsadv.rst000066400000000000000000000020041321511234300212070ustar00rootroot00000000000000Advanced 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.9.0/docs/source/templatesmod.rst000066400000000000000000000152311321511234300222620ustar00rootroot00000000000000Notes 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/breadcrumbs_bootstrap4.html` The same as above but for Bootstrap version 4. * `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_bootstrap4.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_bootstrap4_dropdown.html` The same as above but for Bootstrap version 4. * `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_bootstrap4_navpills.html` The same as above but for Bootstrap version 4. * `sitetree/menu_bootstrap3_navpills-stacked.html` Constructs nav-pills Bootstrap 3 vertical navigation similar to navlist from Bootstrap 2. * `sitetree/menu_bootstrap4_navpills-stacked.html` The same as above but for Bootstrap version 4. 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.9.0/docs/source/thirdparty.rst000066400000000000000000000042551321511234300217620ustar00rootroot00000000000000Thirdparty 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.9.0/setup.cfg000066400000000000000000000001361321511234300164210ustar00rootroot00000000000000[aliases] release = clean --all sdist bdist_wheel upload test = pytest [wheel] universal = 1 django-sitetree-1.9.0/setup.py000077500000000000000000000026061321511234300163210ustar00rootroot00000000000000import os import sys from setuptools import setup from sitetree import VERSION f = open(os.path.join(os.path.dirname(__file__), 'README.rst')) readme = f.read() f.close() PYTEST_RUNNER = ['pytest-runner'] if 'test' in sys.argv else [] 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, setup_requires=[] + PYTEST_RUNNER, tests_require=['pytest', 'pytest-django'], 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.7', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', ], ) django-sitetree-1.9.0/sitetree/000077500000000000000000000000001321511234300164245ustar00rootroot00000000000000django-sitetree-1.9.0/sitetree/__init__.py000066400000000000000000000001131321511234300205300ustar00rootroot00000000000000VERSION = (1, 9, 0) default_app_config = 'sitetree.config.SitetreeConfig'django-sitetree-1.9.0/sitetree/admin.py000066400000000000000000000332531321511234300200740ustar00rootroot00000000000000from django.conf import settings as django_settings from django import VERSION as django_version try: from django.urls import get_urlconf, get_resolver except ImportError: 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' delete_confirmation_template = 'admin/sitetree/treeitem/delete_confirmation.html' def formfield_for_manytomany(self, db_field, request=None, **kwargs): # The same as for GroupAdmin # Avoid a major performance hit resolving permission names which # triggers a content_type load: if db_field.name == 'access_permissions': objects = db_field.remote_field.model.objects if DJANGO_POST_19 else db_field.rel.to.objects qs = kwargs.get('queryset', objects) kwargs['queryset'] = qs.select_related('content_type') return super(TreeItemAdmin, self).formfield_for_manytomany(db_field, request=request, **kwargs) 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. """ path = args[0].path shift = '../' if 'delete' in path: # Weird enough 'delete' is not handled by TreeItemAdmin::response_change(). shift += '../' elif 'history' in path: if 'item_id' not in kwargs: # Encountered request from history page to return to tree layout page. shift += '../' return HttpResponseRedirect(path + 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 = [ url(r'^change/$', 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.9.0/sitetree/compat.py000066400000000000000000000016621321511234300202660ustar00rootroot00000000000000from django import VERSION class CommandOption(object): """Command line option wrapper.""" def __init__(self, *args, **kwargs): self.args = args self.kwargs = kwargs def options_getter(command_options): """Compatibility function to get rid of optparse in management commands after Django 1.10. :param tuple command_options: tuple with `CommandOption` objects. """ def get_options(option_func=None): from optparse import make_option from django.core.management.base import BaseCommand func = option_func or make_option options = tuple([func(*option.args, **option.kwargs) for option in command_options]) if option_func is None: if VERSION < (1, 8): result = BaseCommand.option_list + options else: result = [] else: result = options return result return get_options django-sitetree-1.9.0/sitetree/config.py000066400000000000000000000003311321511234300202400ustar00rootroot00000000000000from 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.9.0/sitetree/exceptions.py000066400000000000000000000001261321511234300211560ustar00rootroot00000000000000 class SiteTreeError(Exception): """Exception class for sitetree application.""" django-sitetree-1.9.0/sitetree/fields.py000066400000000000000000000043461321511234300202530ustar00rootroot00000000000000from django import VERSION from 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 from .settings import ITEMS_FIELD_ROOT_ID 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) context_kwargs = {'current_app': 'admin'} context = template.Context(context_kwargs) if VERSION >= (1, 8) else template.Context(**context_kwargs) context.update({'request': object()}) choices_str = sitetree_tree( Parser(None), Token(token_type=TOKEN_BLOCK, contents=tree_token) ).render(context) tree_choices = [(ITEMS_FIELD_ROOT_ID, 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 try: return MODEL_TREE_ITEM_CLASS.objects.get(pk=value) except MODEL_TREE_ITEM_CLASS.DoesNotExist: return None django-sitetree-1.9.0/sitetree/forms.py000066400000000000000000000014051321511234300201240ustar00rootroot00000000000000from 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.9.0/sitetree/locale/000077500000000000000000000000001321511234300176635ustar00rootroot00000000000000django-sitetree-1.9.0/sitetree/locale/de/000077500000000000000000000000001321511234300202535ustar00rootroot00000000000000django-sitetree-1.9.0/sitetree/locale/de/LC_MESSAGES/000077500000000000000000000000001321511234300220405ustar00rootroot00000000000000django-sitetree-1.9.0/sitetree/locale/de/LC_MESSAGES/django.mo000066400000000000000000000122101321511234300236330ustar00rootroot000000000000005Gl! 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.9.0/sitetree/locale/de/LC_MESSAGES/django.po000066400000000000000000000151171321511234300236470ustar00rootroot00000000000000# 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.9.0/sitetree/locale/en/000077500000000000000000000000001321511234300202655ustar00rootroot00000000000000django-sitetree-1.9.0/sitetree/locale/en/LC_MESSAGES/000077500000000000000000000000001321511234300220525ustar00rootroot00000000000000django-sitetree-1.9.0/sitetree/locale/en/LC_MESSAGES/django.po000066400000000000000000000125411321511234300236570ustar00rootroot00000000000000# 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.9.0/sitetree/locale/es/000077500000000000000000000000001321511234300202725ustar00rootroot00000000000000django-sitetree-1.9.0/sitetree/locale/es/LC_MESSAGES/000077500000000000000000000000001321511234300220575ustar00rootroot00000000000000django-sitetree-1.9.0/sitetree/locale/es/LC_MESSAGES/django.mo000066400000000000000000000131201321511234300236530ustar00rootroot000000000000006I|! 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.9.0/sitetree/locale/es/LC_MESSAGES/django.po000066400000000000000000000160551321511234300236700ustar00rootroot00000000000000# 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.9.0/sitetree/locale/fa/000077500000000000000000000000001321511234300202515ustar00rootroot00000000000000django-sitetree-1.9.0/sitetree/locale/fa/LC_MESSAGES/000077500000000000000000000000001321511234300220365ustar00rootroot00000000000000django-sitetree-1.9.0/sitetree/locale/fa/LC_MESSAGES/django.mo000066400000000000000000000142571321511234300236460ustar00rootroot000000000000006I|! 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.9.0/sitetree/locale/fa/LC_MESSAGES/django.po000066400000000000000000000172171321511234300236500ustar00rootroot00000000000000# 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.9.0/sitetree/locale/fr/000077500000000000000000000000001321511234300202725ustar00rootroot00000000000000django-sitetree-1.9.0/sitetree/locale/fr/LC_MESSAGES/000077500000000000000000000000001321511234300220575ustar00rootroot00000000000000django-sitetree-1.9.0/sitetree/locale/fr/LC_MESSAGES/django.mo000066400000000000000000000141241321511234300236600ustar00rootroot00000000000000=S8z9! .B:5}O C' kw~@@! &08?Vr SY e }    I  ,: Cg     /y $ ) ( !  t/D&L's RCHq  J,w  OCEY^t($ u!q '$= bo ^5=KQ  '0( 93.m(), 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: PO-Revision-Date: 2017-11-19 09:34+0700 Last-Translator: Igor 'idle sign' Starikov Language-Team: French (http://www.transifex.com/idlesign/django-sitetree/language/fr/) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Language: fr Plural-Forms: nplurals=2; plural=(n > 1); X-Generator: Poedit 2.0.4 N'importe laquelle — l'utilisateur doit disposer de l'une des autorisations choisies. Toutes — l'utilisateur doit disposer de toutes les autorisations choisies.Paramètres d'accèsAjouterAjouter un item d'arborescence de siteCommentaires additionnels sur cet item.Paramètres additionnelsAliasToutesN'importe quelleParamètres basiquesFils d'arianeCocher pour accorder l'accès à cet item uniquement aux utilisateurs identifiés.Cocher pour accorder l'accès à cet item uniquement aux visiteurs.Cocher pour restreindre l'accès à cet item aux utilisateurs, en utilisant le système de permissions de Django.SupprimerDescriptionParamètres d'affichageURL exacte ou motif d'URL (voir "Paramètres additionnels") pour cet item.Visiteurs uniquementCachéIndicationHistoriqueAccueilPosition de l'item parmi d'autres items de l'arborescence sous le même parent.Parent de l'item inchangé. L'item ne peut être son propre parent.Connecté seulementMenuDéplacer vers le basDéplacer vers le hautParentItem parent dans l'arborescence de site.Autorisations d'accèsInterprétation des permissionsRestreindre l'accès aux permissionsRestreintCourt nom permettant de récupérer l'arborescence depuis les gabarits.
Note : modifiez avec précaution.Court nom permettant de récupérer l'item d'arborescence depuis un gabarit.
Alias réservés : "%s".Afficher dans le fil d'arianeAfficher dans le menuAfficher dans l'arborescence du siteArborescenceItem d'arborescence de siteItems d'arborescence de siteArborescencesTitre de l'item de l'arborescence. Peut contenir des variables de gabarit ex : {{ montitre }}.Arborescence de site à laquelle cet item appartient.Titre de l'arborescence du site dans un but de présentation.Une information additionnelle à propos de l'item utilisée comme une indication.Ordre de triTitreArborescenceURLURL comme MotifUtilisateurs uniquementL'URL donnée doit-elle être traitée comme un motif ?
Note : Se référer à la documentation Django sur la "Distribution des URL" (section des "Groupes nommés").Doit-on afficher cet item dans un fil d'ariane ?Doit-on afficher cet item dans un menu ?Doit-on afficher cet item dans une arborescence de site ?Doit-on afficher cet item dans la navigation ?Vous voyez cet avertissement car l'option "URL comme Motif" est active et que le motif saisi ci-dessus semble invalide. Noms et paramètres de motifs d'URL actuellement enregistrés :django-sitetree-1.9.0/sitetree/locale/fr/LC_MESSAGES/django.po000066400000000000000000000176031321511234300236700ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # # Translators: # Jean Traullé , 2017 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: 2017-11-19 09:34+0700\n" "Last-Translator: Igor 'idle sign' Starikov \n" "Language-Team: French (http://www.transifex.com/idlesign/django-sitetree/language/fr/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: fr\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" "X-Generator: Poedit 2.0.4\n" #: admin.py:81 msgid "Basic settings" msgstr "Paramètres basiques" #: admin.py:84 msgid "Access settings" msgstr "Paramètres d'accès" #: admin.py:88 msgid "Display settings" msgstr "Paramètres d'affichage" #: admin.py:92 msgid "Additional settings" msgstr "Paramètres additionnels" #: 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 "Vous voyez cet avertissement car l'option \"URL comme Motif\" est active et que le motif saisi ci-dessus semble invalide. Noms et paramètres de motifs d'URL actuellement enregistrés :" #: admin.py:230 msgid "Item's parent left unchanged. Item couldn't be parent to itself." msgstr "Parent de l'item inchangé. L'item ne peut être son propre parent." #: models.py:31 models.py:56 templates/admin/sitetree/tree/change_form.html:41 msgid "Title" msgstr "Titre" #: models.py:31 msgid "Site tree title for presentational purposes." msgstr "Titre de l'arborescence du site dans un but de présentation." #: 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 "Court nom permettant de récupérer l'arborescence depuis les gabarits.
Note : modifiez avec précaution." #: models.py:36 models.py:60 msgid "Site Tree" msgstr "Arborescence" #: models.py:37 msgid "Site Trees" msgstr "Arborescences" #: models.py:52 msgid "Any" msgstr "N'importe quelle" #: models.py:53 msgid "All" msgstr "Toutes" #: models.py:56 msgid "Site tree item title. Can contain template variables E.g.: {{ mytitle }}." msgstr "Titre de l'item de l'arborescence. Peut contenir des variables de gabarit ex : {{ montitre }}." #: models.py:57 msgid "Hint" msgstr "Indication" #: models.py:57 msgid "Some additional information about this item that is used as a hint." msgstr "Une information additionnelle à propos de l'item utilisée comme une indication." #: 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 exacte ou motif d'URL (voir \"Paramètres additionnels\") pour cet item." #: models.py:59 msgid "URL as Pattern" msgstr "URL comme Motif" #: 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 "L'URL donnée doit-elle être traitée comme un motif ?
Note : Se référer à la documentation Django sur la \"Distribution des URL\" (section des \"Groupes nommés\")." #: models.py:60 msgid "Site tree this item belongs to." msgstr "Arborescence de site à laquelle cet item appartient." #: models.py:61 templates/admin/sitetree/tree/change_form.html:34 msgid "Hidden" msgstr "Caché" #: models.py:61 msgid "Whether to show this item in navigation." msgstr "Doit-on afficher cet item dans la navigation ?" #: models.py:62 #, python-format msgid "Short name to address site tree item from a template.
Reserved aliases: \"%s\"." msgstr "Court nom permettant de récupérer l'item d'arborescence depuis un gabarit.
Alias réservés : \"%s\"." #: models.py:63 msgid "Description" msgstr "Description" #: models.py:63 msgid "Additional comments on this item." msgstr "Commentaires additionnels sur cet item." #: models.py:64 msgid "Show in menu" msgstr "Afficher dans le menu" #: models.py:64 msgid "Whether to show this item in a menu." msgstr "Doit-on afficher cet item dans un menu ?" #: models.py:65 msgid "Show in breadcrumb path" msgstr "Afficher dans le fil d'ariane" #: models.py:65 msgid "Whether to show this item in a breadcrumb path." msgstr "Doit-on afficher cet item dans un fil d'ariane ?" #: models.py:66 msgid "Show in site tree" msgstr "Afficher dans l'arborescence du site" #: models.py:66 msgid "Whether to show this item in a site tree." msgstr "Doit-on afficher cet item dans une arborescence de site ?" #: models.py:67 msgid "Logged in only" msgstr "Connecté seulement" #: models.py:67 msgid "Check it to grant access to this item to authenticated users only." msgstr "Cocher pour accorder l'accès à cet item uniquement aux utilisateurs identifiés." #: models.py:68 templates/admin/sitetree/tree/change_form.html:40 msgid "Guests only" msgstr "Visiteurs uniquement" #: models.py:68 msgid "Check it to grant access to this item to guests only." msgstr "Cocher pour accorder l'accès à cet item uniquement aux visiteurs." #: models.py:69 msgid "Restrict access to permissions" msgstr "Restreindre l'accès aux permissions" #: models.py:69 msgid "Check it to restrict user access to this item, using Django permissions system." msgstr "Cocher pour restreindre l'accès à cet item aux utilisateurs, en utilisant le système de permissions de Django." #: models.py:70 msgid "Permissions granting access" msgstr "Autorisations d'accès" #: models.py:71 msgid "Permissions interpretation" msgstr "Interprétation des permissions" #: models.py:71 msgid "Any — user should have any of chosen permissions. All — user should have all chosen permissions." msgstr "N'importe laquelle — l'utilisateur doit disposer de l'une des autorisations choisies. Toutes — l'utilisateur doit disposer de toutes les autorisations choisies." #: models.py:74 msgid "Parent" msgstr "Parent" #: models.py:74 msgid "Parent site tree item." msgstr "Item parent dans l'arborescence de site." #: models.py:75 templates/admin/sitetree/tree/change_form.html:43 msgid "Sort order" msgstr "Ordre de tri" #: models.py:75 msgid "Item position among other site tree items under the same parent." msgstr "Position de l'item parmi d'autres items de l'arborescence sous le même parent." #: models.py:89 msgid "Site Tree Item" msgstr "Item d'arborescence de site" #: models.py:90 templates/admin/sitetree/tree/change_form.html:17 msgid "Site Tree Items" msgstr "Items d'arborescence de site" #: templates/admin/sitetree/tree/change_form.html:24 msgid "Add Site Tree item" msgstr "Ajouter un item d'arborescence de site" #: templates/admin/sitetree/tree/change_form.html:35 msgid "Menu" msgstr "Menu" #: templates/admin/sitetree/tree/change_form.html:36 msgid "Breadcrumbs" msgstr "Fils d'ariane" #: templates/admin/sitetree/tree/change_form.html:37 msgid "Tree" msgstr "Arborescence" #: templates/admin/sitetree/tree/change_form.html:38 msgid "Restricted" msgstr "Restreint" #: templates/admin/sitetree/tree/change_form.html:39 msgid "Users only" msgstr "Utilisateurs uniquement" #: templates/admin/sitetree/tree/tree.html:23 msgid "Move up" msgstr "Déplacer vers le haut" #: templates/admin/sitetree/tree/tree.html:25 msgid "Move down" msgstr "Déplacer vers le bas" #: templates/admin/sitetree/treeitem/breadcrumbs.html:5 msgid "Home" msgstr "Accueil" #: templates/admin/sitetree/treeitem/breadcrumbs.html:11 msgid "Delete" msgstr "Supprimer" #: templates/admin/sitetree/treeitem/breadcrumbs.html:15 msgid "History" msgstr "Historique" #: templates/admin/sitetree/treeitem/breadcrumbs.html:17 msgid "Add" msgstr "Ajouter" django-sitetree-1.9.0/sitetree/locale/nb/000077500000000000000000000000001321511234300202625ustar00rootroot00000000000000django-sitetree-1.9.0/sitetree/locale/nb/LC_MESSAGES/000077500000000000000000000000001321511234300220475ustar00rootroot00000000000000django-sitetree-1.9.0/sitetree/locale/nb/LC_MESSAGES/django.mo000066400000000000000000000132561321511234300236550ustar00rootroot00000000000000=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.9.0/sitetree/locale/nb/LC_MESSAGES/django.po000066400000000000000000000167151321511234300236630ustar00rootroot00000000000000# 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.9.0/sitetree/locale/ru/000077500000000000000000000000001321511234300203115ustar00rootroot00000000000000django-sitetree-1.9.0/sitetree/locale/ru/LC_MESSAGES/000077500000000000000000000000001321511234300220765ustar00rootroot00000000000000django-sitetree-1.9.0/sitetree/locale/ru/LC_MESSAGES/django.mo000066400000000000000000000177461321511234300237140ustar00rootroot00000000000000=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.9.0/sitetree/locale/ru/LC_MESSAGES/django.po000066400000000000000000000233551321511234300237100ustar00rootroot00000000000000# 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.9.0/sitetree/locale/uk/000077500000000000000000000000001321511234300203025ustar00rootroot00000000000000django-sitetree-1.9.0/sitetree/locale/uk/LC_MESSAGES/000077500000000000000000000000001321511234300220675ustar00rootroot00000000000000django-sitetree-1.9.0/sitetree/locale/uk/LC_MESSAGES/django.mo000066400000000000000000000120751321511234300236730ustar00rootroot00000000000000)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.9.0/sitetree/locale/uk/LC_MESSAGES/django.po000066400000000000000000000144201321511234300236720ustar00rootroot00000000000000# 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.9.0/sitetree/management/000077500000000000000000000000001321511234300205405ustar00rootroot00000000000000django-sitetree-1.9.0/sitetree/management/__init__.py000066400000000000000000000000011321511234300226400ustar00rootroot00000000000000 django-sitetree-1.9.0/sitetree/management/commands/000077500000000000000000000000001321511234300223415ustar00rootroot00000000000000django-sitetree-1.9.0/sitetree/management/commands/__init__.py000066400000000000000000000000011321511234300244410ustar00rootroot00000000000000 django-sitetree-1.9.0/sitetree/management/commands/sitetree_resync_apps.py000066400000000000000000000057241321511234300271550ustar00rootroot00000000000000from 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 from sitetree.compat import CommandOption, options_getter MODEL_TREE_CLASS = get_tree_model() get_options = options_getter(( CommandOption( '--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.' ), )) 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 = get_options() def add_arguments(self, parser): parser.add_argument('args', metavar='app', nargs='*', help='Application names.') get_options(parser.add_argument) 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 if hasattr(item.access_permissions, 'set'): item.access_permissions.set(item.permissions) else: item.access_permissions = item.permissions Cache.reset() django-sitetree-1.9.0/sitetree/management/commands/sitetreedump.py000066400000000000000000000040041321511234300254230ustar00rootroot00000000000000from 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 from sitetree.compat import CommandOption, options_getter MODEL_TREE_CLASS = get_tree_model() MODEL_TREE_ITEM_CLASS = get_tree_item_model() get_options = options_getter(( CommandOption( '--indent', default=None, dest='indent', type=int, help='Specifies the indent level to use when pretty-printing output.'), CommandOption('--items_only', action='store_true', dest='items_only', default=False, help='Export tree items only.'), CommandOption('--database', action='store', dest='database', default=DEFAULT_DB_ALIAS, help='Nominates a specific database to export fixtures from. Defaults to the "default" database.'), )) class Command(BaseCommand): option_list = get_options() help = 'Output sitetrees from database as a fixture in JSON format.' args = '[tree_alias tree_alias ...]' def add_arguments(self, parser): parser.add_argument('args', metavar='tree', nargs='?', help='Tree aliases.', default=[]) get_options(parser.add_argument) 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.9.0/sitetree/management/commands/sitetreeload.py000066400000000000000000000163331321511234300254050ustar00rootroot00000000000000from collections import defaultdict import sys from django import VERSION from django.core import serializers from django.core.exceptions import ObjectDoesNotExist 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 sitetree.compat import CommandOption, options_getter 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() VER_LESS_18 = VERSION < (1, 8) get_options = options_getter(( CommandOption( '--database', action='store', dest='database', default=DEFAULT_DB_ALIAS, help='Nominates a specific database to load fixtures into. ' 'Defaults to the "default" database.'), CommandOption( '--mode', action='store', dest='mode', default='append', help='Mode to put data into DB. Variants: `replace`, `append`.'), CommandOption( '--items_into_tree', action='store', dest='items_into_tree', default=None, help='Import only tree items data into tree with given alias.'), )) class Command(BaseCommand): option_list = get_options() help = 'Loads sitetrees from fixture in JSON format into database.' args = '[fixture_file fixture_file ...]' def add_arguments(self, parser): parser.add_argument('args', metavar='fixture', nargs='+', help='Fixture files.') get_options(parser.add_argument) def handle(self, *fixture_files, **options): using = options.get('database', DEFAULT_DB_ALIAS) mode = options.get('mode', 'append') items_into_tree = options.get('items_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() if VER_LESS_18: transaction.enter_transaction_management(using=using) loaded_object_count = 0 if mode == 'replace': MODEL_TREE_CLASS.objects.all().delete() MODEL_TREE_ITEM_CLASS.objects.all().delete() 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 = [] # Parents go first: enough for simple cases. tree_items[orig_tree_id].sort(key=lambda item: item.id not in tree_item_parents.keys()) 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 VER_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_info())) )) ) 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 VER_LESS_18: transaction.commit(using=using) transaction.leave_transaction_management(using=using) connection.close() django-sitetree-1.9.0/sitetree/migrations/000077500000000000000000000000001321511234300206005ustar00rootroot00000000000000django-sitetree-1.9.0/sitetree/migrations/0001_initial.py000066400000000000000000000121141321511234300232420ustar00rootroot00000000000000# -*- 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', on_delete=models.CASCADE, blank=True, to='sitetree.TreeItem', help_text='Parent site tree item.', null=True, verbose_name='Parent')), ('tree', models.ForeignKey(related_name='treeitem_tree', on_delete=models.CASCADE, 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.9.0/sitetree/migrations/__init__.py000066400000000000000000000000001321511234300226770ustar00rootroot00000000000000django-sitetree-1.9.0/sitetree/models.py000066400000000000000000000144121321511234300202630ustar00rootroot00000000000000from django.contrib.auth.models import Permission from django.db import models from django.utils.encoding import python_2_unicode_compatible from django.utils.translation import ugettext_lazy as _ from .settings import MODEL_TREE, TREE_ITEMS_ALIASES 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', on_delete=models.CASCADE, 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', on_delete=models.CASCADE, 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): """Ensure that item is not its own parent and set proper sort order. """ # Ensure that item is not its own parent, since this breaks # the sitetree (and possibly the entire site). if self.parent == self: self.parent = None # Set item's sort order to its primary key. id_ = self.id if id_ and self.sort_order == 0: self.sort_order = id_ super(TreeItemBase, self).save(force_insert, force_update, **kwargs) # Set item's sort order to its primary key if not already set. 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.9.0/sitetree/runtests.py000077500000000000000000000002061321511234300206660ustar00rootroot00000000000000#! /usr/bin/env python import sys if __name__ == '__main__': from pytest import main as pytest_main sys.exit(pytest_main()) django-sitetree-1.9.0/sitetree/settings.py000066400000000000000000000030331321511234300206350ustar00rootroot00000000000000from django.conf import settings MODEL_TREE = getattr(settings, 'SITETREE_MODEL_TREE', 'sitetree.Tree') """Path to a tree model (app.class).""" MODEL_TREE_ITEM = getattr(settings, 'SITETREE_MODEL_TREE_ITEM', 'sitetree.TreeItem') """Path to a tree item model (app.class).""" APP_MODULE_NAME = getattr(settings, 'SITETREE_APP_MODULE_NAME', 'sitetrees') """Module name where applications store trees shipped with them.""" UNRESOLVED_ITEM_MARKER = getattr(settings, 'SITETREE_UNRESOLVED_ITEM_MARKER', u'#unresolved') """This string is place instead of item URL if actual URL cannot be resolved.""" RAISE_ITEMS_ERRORS_ON_DEBUG = getattr(settings, 'SITETREE_RAISE_ITEMS_ERRORS_ON_DEBUG', True) ITEMS_FIELD_ROOT_ID = getattr(settings, 'SITETREE_ITEMS_FIELD_ROOT_ID', '') """Item ID to be used for root item in TreeItemChoiceField. This is adjustable to be able to workaround client-side field validation issues in thirdparties. """ CACHE_TIMEOUT = getattr(settings, 'SITETREE_CACHE_TIMEOUT', 31536000) """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. """ # 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.9.0/sitetree/sitetreeapp.py000066400000000000000000001111321321511234300213220ustar00rootroot00000000000000from __future__ import unicode_literals import warnings from collections import defaultdict from copy import deepcopy from functools import partial from threading import local from django import VERSION from django.conf import settings from django.core.cache import cache from django.db.models import signals 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 django.template.loader import get_template from django.utils import six from django.utils.encoding import python_2_unicode_compatible from django.utils.http import urlquote from django.utils.translation import get_language from .exceptions import SiteTreeError 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) from .utils import get_tree_model, get_tree_item_model, import_app_sitetree_module, generate_id_for if False: # pragma: nocover from django.template import Context from .models import TreeItemBase 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() _ITEMS_PROCESSOR = None """Stores tree items processor callable or None.""" _I18N_TREES = [] """Stores aliases of trees supporting internationalization.""" _DYNAMIC_TREES = {} """Holds trees dynamically loaded from project apps.""" _IDX_ORPHAN_TREES = 'orphans' """Dictionary index in `_DYNAMIC_TREES` for orphaned trees list.""" _IDX_TPL = '%s|:|%s' """Name template used as dictionary index in `_DYNAMIC_TREES`.""" _THREAD_LOCAL = local() _THREAD_SITETREE = 'sitetree' _CONTEXT_FLATTEN = VERSION >= (1, 11) _UNSET = set() # Sentinel def get_sitetree(): """Returns SiteTree (thread-singleton) object, implementing utility methods. :rtype: SiteTree """ sitetree = getattr(_THREAD_LOCAL, _THREAD_SITETREE, None) if sitetree is None: sitetree = SiteTree() setattr(_THREAD_LOCAL, _THREAD_SITETREE, sitetree) return sitetree def register_items_hook(func): """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) :param func: """ global _ITEMS_PROCESSOR _ITEMS_PROCESSOR = func 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']) :param aliases: """ 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: 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, :param str|iterable src: 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. `src` can be an iterable of tree definitions (see `sitetree.toolbox.tree()` and `item()` functions). :param str|unicode target_tree_alias: Static tree alias to attach items from dynamic trees to. :param str|unicode parent_tree_item_alias: Tree item alias from a static tree to attach items from dynamic trees to. :param list include_trees: Sitetree aliases to filter `src`. :rtype: dict """ 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 to be an application name. try: module = import_app_sitetree_module(src) return None if module is None else 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)) return None return result() @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): """ :param str|unicode 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(get_sitetree().current_page_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) self.init() @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.set('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(init=False) self.cache = cache.get( 'sitetrees', {'sitetrees': {}, 'parents': {}, 'items_by_ids': {}, 'tree_aliases': {}}) def save(self): """Saves sitetree data to Django cache.""" cache.set('sitetrees', self.cache, CACHE_TIMEOUT) def empty(self, **kwargs): """Empties cached sitetree data.""" cache.delete('sitetrees') cache.delete('sitetrees_reset') kwargs.get('init', True) and self.init() def get_entry(self, entry_name, key): """Returns cache entry parameter value by its name. :param str|unicode entry_name: :param key: :return: """ return self.cache[entry_name].get(key, False) def update_entry_value(self, entry_name, key, value): """Updates cache entry parameter with new data. :param str|unicode entry_name: :param key: :param value: """ 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. :param str|unicode entry_name: :param key: :param value: """ self.cache[entry_name][key] = value class SiteTree(object): """Main logic handler.""" def __init__(self): self.init(context=None) def init(self, context): """Initializes sitetree to handle new request. :param Context|None context: """ self.cache = Cache() self.current_page_context = context self.current_request = context.get('request', None) if context else None self.current_lang = get_language() self._current_app_is_admin = None self._current_user_permissions = _UNSET self._items_urls = {} # Resolved urls are cache for a request. self._current_items = {} 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. :param str|unicode alias: :rtype: str|unicode """ if alias not in _I18N_TREES: return alias current_language_code = self.current_lang 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. :param str|unicode tree_alias: :param list src_tree_items: :rtype: 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 not static_item.alias: continue idx = _IDX_TPL % (tree_alias, static_item.alias) if idx not in trees: continue 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. :rtype: bool """ is_admin = self._current_app_is_admin if is_admin is None: context = self.current_page_context current_app = getattr( # Try from request.resolver_match.app_name getattr(context.get('request', None), 'resolver_match', None), 'app_name', # Try from global context obj. getattr(context, 'current_app', None)) if current_app is None: # Try from global context dict. current_app = context.get('current_app', '') is_admin = current_app == 'admin' self._current_app_is_admin = is_admin return is_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. :param str|unicode alias: :rtype: tuple """ cache_ = self.cache get_cache_entry = cache_.get_entry set_cache_entry = cache_.set_entry caching_required = 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'). prefetch_related('access_permissions__content_type'). 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) caching_required = 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 caching_required: # 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 caching_required: 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.all()) 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) item.title_resolved = LazyTitle(item.title) if VARIABLE_TAG_START in item.title else 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 caching_required: cache_.save() return alias, sitetree def calculate_item_depth(self, tree_alias, item_id, depth=0): """Calculates depth of the item in the tree. :param str|unicode tree_alias: :param int item_id: :param int depth: :rtype: int """ item = self.get_item_by_id(tree_alias, item_id) if hasattr(item, 'depth'): depth = item.depth + depth else: if item.parent is not None: depth = self.calculate_item_depth(tree_alias, item.parent.id, depth + 1) return depth def get_item_by_id(self, tree_alias, item_id): """Get the item from the tree by its ID. :param str|unicode tree_alias: :param int item_id: :rtype: TreeItemBase """ 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. :param str|unicode tree_alias: :rtype: TreeItemBase """ current_item = self._current_items.get(tree_alias, _UNSET) if current_item is not _UNSET: if current_item is not None: current_item.is_current = True # Could be reset by .get_sitetree() return current_item current_item = None if self.current_app_is_admin(): self._current_items[tree_alias] = current_item return None # urlquote is an attempt to support non-ascii in url. current_url = self.current_request.path if isinstance(current_url, str): current_url = current_url.encode('UTF-8') if current_url: current_url = urlquote(current_url) for url_item, url in self._items_urls.items(): # Iterate each as this dict may contains "current" items for various trees. if url != current_url: continue url_item.is_current = True if url_item.tree.alias == tree_alias: current_item = url_item if current_item is not None: self._current_items[tree_alias] = current_item return current_item def url(self, sitetree_item, context=None): """Resolves item's URL. :param TreeItemBase sitetree_item: TreeItemBase heir object, 'url' property of which is processed as URL pattern or simple URL. :param Context context: :rtype: str|unicode """ context = context or self.current_page_context if not isinstance(sitetree_item, MODEL_TREE_ITEM_CLASS): sitetree_item = self.resolve_var(sitetree_item, context) resolved_url = self._items_urls.get(sitetree_item) if resolved_url is not None: return resolved_url # 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"' % resolved) view_path = view_path[0].strip('"\' ') url_pattern = "'%s' %s" % (view_path, ' '.join(all_arguments)) else: url_pattern = '%s' % sitetree_item.url if sitetree_item.urlaspattern: # Form token to pass to Django 'url' tag. url_token = 'url %s as item.url_resolved' % url_pattern url_tag( Parser(None), Token(token_type=TOKEN_BLOCK, contents=url_token) ).render(context) resolved_url = context['item.url_resolved'] or UNRESOLVED_ITEM_MARKER else: resolved_url = url_pattern self._items_urls[sitetree_item] = resolved_url return resolved_url def init_tree(self, tree_alias, context): """Initializes sitetree in memory. Returns tuple with resolved tree alias and items on success. On fail returns (None, None). :param str|unicode tree_alias: :param Context context: :rtype: tuple """ request = context.get('request', None) if request is None: raise SiteTreeError( 'Sitetree requires "django.core.context_processors.request" template context processor to be active. ' 'If it is, check that your view pushes request data into the template.') if id(request) != id(self.current_request): self.init(context) # Resolve tree_alias from the context. tree_alias = self.resolve_var(tree_alias) tree_alias, sitetree_items = self.get_sitetree(tree_alias) if not sitetree_items: return None, None return tree_alias, sitetree_items def get_current_page_title(self, tree_alias, context): """Returns resolved from sitetree title for current page. :param str|unicode tree_alias: :param Context context: :rtype: str|unicode """ 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. :param str|unicode attr_name: :param str|unicode tree_alias: :param Context context: :rtype: str|unicode """ tree_alias, sitetree_items = self.init_tree(tree_alias, context) current_item = self.get_tree_current_item(tree_alias) 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, depth=1): """Returns ancestor of level `deep` recursively :param TreeItemBase current_item: :param int depth: :rtype: TreeItemBase """ if current_item.parent is None: return current_item if depth <= 1: return current_item.parent return self.get_ancestor_level(current_item.parent, depth=depth-1) def menu(self, tree_alias, tree_branches, context): """Builds and returns menu structure for 'sitetree_menu' tag. :param str|unicode tree_alias: :param str|unicode tree_branches: :param Context context: :rtype: list|str """ tree_alias, sitetree_items = self.init_tree(tree_alias, context) 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, depth=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) menu_items = self.apply_hook(menu_items, 'menu') self.update_has_children(tree_alias, menu_items, 'menu') return menu_items @classmethod def apply_hook(cls, 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. :param list items: :param str|unicode sender: menu, breadcrumbs, sitetree, {type}.children, {type}.has_children :rtype: list """ 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. :param TreeItemBase item: :param Context context: :rtype: bool """ if hasattr(self.current_request.user.is_authenticated, '__call__'): authenticated = self.current_request.user.is_authenticated() else: authenticated = self.current_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 = self._current_user_permissions if user_perms is _UNSET: user_perms = set(context['user'].get_all_permissions()) self._current_user_permissions = user_perms 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. :param str|unicode tree_alias: :param Context context: :rtype: list|str """ tree_alias, sitetree_items = self.init_tree(tree_alias, context) if not sitetree_items: return '' current_item = self.get_tree_current_item(tree_alias) breadcrumbs = [] if current_item is not None: context_ = self.current_page_context check_access = self.check_access get_item_by_id = self.get_item_by_id def climb(base_item): """Climbs up the site tree to build breadcrumb path. :param TreeItemBase base_item: """ if base_item.inbreadcrumbs and not base_item.hidden and check_access(base_item, context_): breadcrumbs.append(base_item) if hasattr(base_item, 'parent') and base_item.parent is not None: climb(get_item_by_id(tree_alias, base_item.parent.id)) climb(current_item) breadcrumbs.reverse() items = self.apply_hook(breadcrumbs, 'breadcrumbs') 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. :param str|unicode tree_alias: :param Context context: :rtype: list|str """ tree_alias, sitetree_items = self.init_tree(tree_alias, context) 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') 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. :param TreeItemBase parent_item: :param str|unicode navigation_type: menu, sitetree :param str|unicode use_template: :param Context context: :rtype: list """ # 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) self.update_has_children(tree_alias, tree_items, navigation_type) my_template = get_template(use_template) context.push() context['sitetree_items'] = tree_items rendered = my_template.render(context.flatten() if _CONTEXT_FLATTEN else context) context.pop() return rendered def get_children(self, tree_alias, item): """Returns item's children. :param str|unicode tree_alias: :param TreeItemBase|None item: :rtype: list """ 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 inplace. :param str|unicode tree_alias: :param list tree_items: :param str|unicode navigation_type: sitetree, breadcrumbs, menu """ 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 def filter_items(self, items, navigation_type=None): """Filters sitetree item's children if hidden and by navigation type. NB: We do not apply any filters to sitetree in admin app. :param list items: :param str|unicode navigation_type: sitetree, breadcrumbs, menu :rtype: list """ if self.current_app_is_admin(): return items items_filtered = [] context = self.current_page_context check_access = self.check_access for item in items: if item.hidden: continue if not check_access(item, context): continue if not getattr(item, 'in%s' % navigation_type, True): # Hidden for current nav type continue items_filtered.append(item) return items_filtered def get_ancestor_item(self, tree_alias, base_item): """Climbs up the site tree to resolve root item for chosen one. :param str|unicode tree_alias: :param TreeItemBase base_item: :rtype: TreeItemBase """ parent = None if hasattr(base_item, 'parent') and base_item.parent is not None: parent = self.get_ancestor_item(tree_alias, self.get_item_by_id(tree_alias, base_item.parent.id)) if parent is None: return base_item return parent def tree_climber(self, tree_alias, base_item): """Climbs up the site tree to mark items of current branch. :param str|unicode tree_alias: :param TreeItemBase base_item: """ if base_item is not None: base_item.in_current_branch = True if hasattr(base_item, 'parent') and base_item.parent is not None: self.tree_climber(tree_alias, self.get_item_by_id(tree_alias, base_item.parent.id)) def resolve_var(self, varname, context=None): """Resolves name as a variable in a given context. If no context specified page context' is considered as context. :param str|unicode varname: :param Context context: :return: """ context = context or self.current_page_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 django-sitetree-1.9.0/sitetree/templates/000077500000000000000000000000001321511234300204225ustar00rootroot00000000000000django-sitetree-1.9.0/sitetree/templates/admin/000077500000000000000000000000001321511234300215125ustar00rootroot00000000000000django-sitetree-1.9.0/sitetree/templates/admin/sitetree/000077500000000000000000000000001321511234300233365ustar00rootroot00000000000000django-sitetree-1.9.0/sitetree/templates/admin/sitetree/tree/000077500000000000000000000000001321511234300242755ustar00rootroot00000000000000django-sitetree-1.9.0/sitetree/templates/admin/sitetree/tree/change_form.html000066400000000000000000000025751321511234300274440ustar00rootroot00000000000000{% 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.9.0/sitetree/templates/admin/sitetree/tree/change_list_.html000066400000000000000000000006011321511234300275770ustar00rootroot00000000000000{% 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.9.0/sitetree/templates/admin/sitetree/tree/tree.html000066400000000000000000000047031321511234300261260ustar00rootroot00000000000000{% 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.9.0/sitetree/templates/admin/sitetree/tree/tree_combo.html000066400000000000000000000005071321511234300273030ustar00rootroot00000000000000{% 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.9.0/sitetree/templates/admin/sitetree/treeitem/000077500000000000000000000000001321511234300251545ustar00rootroot00000000000000django-sitetree-1.9.0/sitetree/templates/admin/sitetree/treeitem/breadcrumbs.html000066400000000000000000000017321321511234300303360ustar00rootroot00000000000000{% load i18n %} {% load sitetree_compat %} django-sitetree-1.9.0/sitetree/templates/admin/sitetree/treeitem/change_form.html000066400000000000000000000037701321511234300303210ustar00rootroot00000000000000{% 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.9.0/sitetree/templates/admin/sitetree/treeitem/delete_confirmation.html000066400000000000000000000002141321511234300320510ustar00rootroot00000000000000{% extends "admin/delete_confirmation.html" %} {% block breadcrumbs %}{% include "admin/sitetree/treeitem/breadcrumbs.html" %}{% endblock %}django-sitetree-1.9.0/sitetree/templates/admin/sitetree/treeitem/object_history.html000066400000000000000000000002071321511234300310700ustar00rootroot00000000000000{% extends "admin/object_history.html" %} {% block breadcrumbs %}{% include "admin/sitetree/treeitem/breadcrumbs.html" %}{% endblock %}django-sitetree-1.9.0/sitetree/templates/sitetree/000077500000000000000000000000001321511234300222465ustar00rootroot00000000000000django-sitetree-1.9.0/sitetree/templates/sitetree/breadcrumbs-title.html000066400000000000000000000002731321511234300265460ustar00rootroot00000000000000{% 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.9.0/sitetree/templates/sitetree/breadcrumbs.html000066400000000000000000000005761321511234300254350ustar00rootroot00000000000000{% 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.9.0/sitetree/templates/sitetree/breadcrumbs_bootstrap.html000066400000000000000000000006431321511234300275250ustar00rootroot00000000000000{% load sitetree %} {% if sitetree_items|length_is:"1" %} {% else %} {% endif %}django-sitetree-1.9.0/sitetree/templates/sitetree/breadcrumbs_bootstrap3.html000066400000000000000000000006041321511234300276050ustar00rootroot00000000000000{% load sitetree %} {% if sitetree_items|length_is:"1" %} {% else %} {% endif %}django-sitetree-1.9.0/sitetree/templates/sitetree/breadcrumbs_bootstrap4.html000066400000000000000000000010071321511234300276040ustar00rootroot00000000000000{% load sitetree %} {% if sitetree_items|length_is:"1" %} {% else %} {% endif %}django-sitetree-1.9.0/sitetree/templates/sitetree/breadcrumbs_foundation.html000066400000000000000000000006261321511234300276570ustar00rootroot00000000000000{% load sitetree %} {% if sitetree_items|length_is:"1" %} {% else %} {% endif %}django-sitetree-1.9.0/sitetree/templates/sitetree/breadcrumbs_semantic.html000066400000000000000000000006671321511234300273210ustar00rootroot00000000000000{% load sitetree %} {% if sitetree_items|length_is:"1" %} {% else %} {% endif %}django-sitetree-1.9.0/sitetree/templates/sitetree/menu.html000066400000000000000000000007671321511234300241120ustar00rootroot00000000000000{% 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.9.0/sitetree/templates/sitetree/menu_bootstrap.html000066400000000000000000000013331321511234300261750ustar00rootroot00000000000000{% load sitetree %} django-sitetree-1.9.0/sitetree/templates/sitetree/menu_bootstrap3.html000066400000000000000000000013471321511234300262650ustar00rootroot00000000000000{% load sitetree %} django-sitetree-1.9.0/sitetree/templates/sitetree/menu_bootstrap3_dropdown.html000066400000000000000000000006061321511234300301760ustar00rootroot00000000000000{% load sitetree %} django-sitetree-1.9.0/sitetree/templates/sitetree/menu_bootstrap3_navpills-stacked.html000066400000000000000000000004551321511234300316100ustar00rootroot00000000000000{% load sitetree %} django-sitetree-1.9.0/sitetree/templates/sitetree/menu_bootstrap3_navpills.html000066400000000000000000000004411321511234300301670ustar00rootroot00000000000000{% load sitetree %} django-sitetree-1.9.0/sitetree/templates/sitetree/menu_bootstrap4.html000066400000000000000000000013421321511234300262610ustar00rootroot00000000000000{% load sitetree %} django-sitetree-1.9.0/sitetree/templates/sitetree/menu_bootstrap4_dropdown.html000066400000000000000000000005571321511234300302040ustar00rootroot00000000000000{% load sitetree %} django-sitetree-1.9.0/sitetree/templates/sitetree/menu_bootstrap4_navpills-stacked.html000066400000000000000000000005071321511234300316070ustar00rootroot00000000000000{% load sitetree %} django-sitetree-1.9.0/sitetree/templates/sitetree/menu_bootstrap4_navpills.html000066400000000000000000000004731321511234300301750ustar00rootroot00000000000000{% load sitetree %} django-sitetree-1.9.0/sitetree/templates/sitetree/menu_bootstrap_dropdown.html000066400000000000000000000005261321511234300301140ustar00rootroot00000000000000{% load sitetree %} django-sitetree-1.9.0/sitetree/templates/sitetree/menu_bootstrap_navlist.html000066400000000000000000000004401321511234300277330ustar00rootroot00000000000000{% load sitetree %} django-sitetree-1.9.0/sitetree/templates/sitetree/menu_foundation-vertical.html000066400000000000000000000010641321511234300301360ustar00rootroot00000000000000{% load sitetree %} django-sitetree-1.9.0/sitetree/templates/sitetree/menu_foundation.html000066400000000000000000000010531321511234300263250ustar00rootroot00000000000000{% load sitetree %} django-sitetree-1.9.0/sitetree/templates/sitetree/menu_foundation_flyout.html000066400000000000000000000004671321511234300277370ustar00rootroot00000000000000{% load sitetree %} django-sitetree-1.9.0/sitetree/templates/sitetree/menu_foundation_sidenav.html000066400000000000000000000004041321511234300300350ustar00rootroot00000000000000{% load sitetree %} django-sitetree-1.9.0/sitetree/templates/sitetree/menu_semantic-vertical.html000066400000000000000000000012101321511234300275640ustar00rootroot00000000000000{% load sitetree %} django-sitetree-1.9.0/sitetree/templates/sitetree/menu_semantic.html000066400000000000000000000010621321511234300257620ustar00rootroot00000000000000{% load sitetree %} {% for item in sitetree_items %} {% if item.has_children %} {% else %} {{ item.title_resolved }} {% endif %} {% endfor %}django-sitetree-1.9.0/sitetree/templates/sitetree/menu_semantic_dropdown.html000066400000000000000000000004651321511234300277040ustar00rootroot00000000000000{% load sitetree %} django-sitetree-1.9.0/sitetree/templates/sitetree/tree.html000066400000000000000000000006511321511234300240750ustar00rootroot00000000000000{% 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.9.0/sitetree/templatetags/000077500000000000000000000000001321511234300211165ustar00rootroot00000000000000django-sitetree-1.9.0/sitetree/templatetags/__init__.py000066400000000000000000000000001321511234300232150ustar00rootroot00000000000000django-sitetree-1.9.0/sitetree/templatetags/sitetree.py000066400000000000000000000246401321511234300233220ustar00rootroot00000000000000from django import template from django.template.loader import get_template from django.template.base import FilterExpression from ..sitetreeapp import get_sitetree, _CONTEXT_FLATTEN register = template.Library() @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 = get_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 get_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 = get_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 = get_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 get_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 get_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 get_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 get_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.flatten() if _CONTEXT_FLATTEN else context) context.pop() return content django-sitetree-1.9.0/sitetree/templatetags/sitetree_compat.py000066400000000000000000000003101321511234300246510ustar00rootroot00000000000000from 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.9.0/sitetree/tests/000077500000000000000000000000001321511234300175665ustar00rootroot00000000000000django-sitetree-1.9.0/sitetree/tests/__init__.py000066400000000000000000000001071321511234300216750ustar00rootroot00000000000000# This package is considered both as a django app, and a test package. django-sitetree-1.9.0/sitetree/tests/common.py000066400000000000000000000003671321511234300214360ustar00rootroot00000000000000import re RE_TAG_VALUES = re.compile('>([^<]+)<') def strip_tags(src): result = [] for match in RE_TAG_VALUES.findall(src): match = match.strip() if match: result.append(match) return '|'.join(result) django-sitetree-1.9.0/sitetree/tests/conftest.py000066400000000000000000000174131321511234300217730ustar00rootroot00000000000000import os try: from unittest import mock except ImportError: import mock import pytest from django.http import HttpRequest from django.conf import settings, global_settings from django import VERSION from django.template.context import Context, RenderContext from django.template.base import Template def pytest_configure(): app_name = os.path.basename(os.path.dirname(os.path.dirname(__file__))) if not pytest.config.pluginmanager.hasplugin('django'): raise Exception('`pytest-django` package is required to run `%s` tests.' % app_name) if hasattr(global_settings, "MIDDLEWARE"): middleware = global_settings.MIDDLEWARE else: middleware = global_settings.MIDDLEWARE_CLASSES configure_kwargs = dict( INSTALLED_APPS=( 'django.contrib.auth', 'django.contrib.contenttypes', app_name, '%s.tests' % app_name ), DATABASES={'default': {'ENGINE': 'django.db.backends.sqlite3', 'NAME': ':memory:'}}, MIDDLEWARE_CLASSES=middleware, # Prevents Django 1.7 warning. ROOT_URLCONF='%s.tests.urls' % app_name, TEMPLATES=[{ 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'APP_DIRS': True, }], ) try: # Django < 1.8 configure_kwargs['TEMPLATE_CONTEXT_PROCESSORS'] = tuple(global_settings.TEMPLATE_CONTEXT_PROCESSORS) + ( 'django.core.context_processors.request', ) except AttributeError: pass settings.configure(**configure_kwargs) class MockUser(object): def __init__(self, user_data): authorized = user_data permissions = None if isinstance(user_data, tuple): authorized, permissions = user_data self.authorized = authorized self.permissions = permissions def is_authenticated(self): return self.authorized def get_all_permissions(self): return self.permissions class MockRequest(HttpRequest): def __init__(self, path='/', user=None, meta=None): self.path = path self.user = user self.META = meta def contribute_to_context(context, current_app=''): template = mock.MagicMock() template.engine.string_if_invalid = '' context.template = template if VERSION >= (1, 11): context.render_context = RenderContext() if VERSION >= (1, 10): match = mock.MagicMock() match.app_name = current_app context.resolver_match = match else: context._current_app = current_app @pytest.fixture def mock_request(): def get_request(**kwargs): return MockRequest(**kwargs) return get_request @pytest.fixture def mock_template_context(): def get_context(context_dict=None, current_app='', request_path=None, user_data=None): user = user_data if hasattr(user_data, '_meta') else MockUser(user_data) context_dict = context_dict or {} context_updater = { 'request': MockRequest(request_path, user), } if user_data: context_updater['user'] = user context_dict.update(context_updater) context = Context(context_dict) contribute_to_context(context, current_app) return context return get_context @pytest.fixture def build_tree(): """Builds a sitetree from dict definition. Returns items indexed by urls. Example: items_map = build_tree( {'alias': 'mytree'}, [{ 'title': 'one', 'url': '/one/', 'children': [ {'title': 'subone', 'url': '/subone/'} ] }] ) """ from sitetree.models import Tree, TreeItem from django.contrib.auth.models import Permission def build(tree_dict, items): def attach_items(tree, items, parent=None): for item_dict in items: children = item_dict.pop('children', []) access_permissions = item_dict.pop('access_permissions', []) item = TreeItem(**item_dict) item.tree = tree item.parent = parent item.save() for permission in access_permissions: item.access_permissions.add(Permission.objects.get(codename=permission)) items_map['%s' % item.url] = item children and attach_items(tree, children, parent=item) items_map = {} tree = Tree(**tree_dict) tree.save() attach_items(tree, items) return items_map return build @pytest.fixture def render_template_tag(): """Renders a template tag from a given library by its name. Example: render_template_tag('library_name', 'mytag arg1 arg2') """ def render(tag_library, tag_str, context=None): context = context or {} if not isinstance(context, Context): context = Context(context) contribute_to_context(context) string = '{%% load %s %%}{%% %s %%}' % (tag_library, tag_str) template = Template(string) if VERSION >= (1, 11): # Prevent "TypeError: 'NoneType' object is not iterable" in get_exception_info template.nodelist[1].token.position = (0, 0) return template.render(context) return render @pytest.fixture def common_tree(build_tree): items = build_tree( {'alias': 'mytree'}, [{ 'title': 'Home', 'url': '/home/', 'children': [ {'title': 'Users', 'url': '/users/', 'children': [ {'title': 'Moderators', 'url': '/users/moderators/'}, {'title': 'Ordinary', 'url': '/users/ordinary/'}, {'title': 'Hidden', 'hidden': True, 'url': '/users/hidden/'}, ]}, {'title': 'Articles', 'url': '/articles/', 'children': [ {'title': 'About cats', 'url': '/articles/cats/', 'children': [ {'title': 'Good', 'url': '/articles/cats/good/'}, {'title': 'Bad', 'url': '/articles/cats/bad/'}, {'title': 'Ugly', 'url': '/articles/cats/ugly/'}, ]}, {'title': 'About dogs', 'url': '/articles/dogs/'}, {'title': 'About mice', 'inmenu': False, 'url': '/articles/mice/'}, ]}, {'title': 'Contacts', 'inbreadcrumbs': False, 'url': '/contacts/', 'children': [ {'title': 'Russia', 'url': '/contacts/russia/', 'hint': 'The place', 'description': 'Russian Federation', 'children': [ {'title': 'Web', 'alias': 'ruweb', 'url': '/contacts/russia/web/', 'children': [ {'title': 'Public {{ subtitle }}', 'url': '/contacts/russia/web/public/'}, {'title': 'Private', 'url': '/contacts/russia/web/private/', 'hint': 'Private Area Hint', 'description': 'Private Area Description', }, ]}, {'title': 'Postal', 'insitetree': False, 'url': '/contacts/russia/postal/'}, ]}, {'title': 'Australia', 'urlaspattern': True, 'url': 'contacts_australia australia_var', 'children': [ {'title': 'Alice Springs', 'access_loggedin': True, 'url': '/contacts/australia/alice/'}, {'title': 'Darwin', 'access_guest': True, 'url': '/contacts/australia/darwin/'}, ]}, {'title': 'China', 'urlaspattern': True, 'url': 'contacts_china china_var'}, ]}, ] }] ) items[''] = items['/home/'] return items django-sitetree-1.9.0/sitetree/tests/sitetrees.py000066400000000000000000000004161321511234300221500ustar00rootroot00000000000000from sitetree.toolbox import tree, item sitetrees = [ tree('dynamic3', items=[ item('dynamic3_1', '/dynamic3_1_url', url_as_pattern=False), ]), tree('dynamic4', items=[ item('dynamic4_1', '/dynamic4_1_url', url_as_pattern=False), ]), ] django-sitetree-1.9.0/sitetree/tests/test_admin.py000066400000000000000000000064541321511234300223000ustar00rootroot00000000000000#! -*- encoding: utf-8 -*- from __future__ import unicode_literals import pytest from django.contrib.admin.sites import site def get_item_admin(): from sitetree.models import TreeItem from sitetree.admin import TreeItemAdmin admin = TreeItemAdmin(TreeItem, site) return admin @pytest.mark.django_db def test_admin_tree_item_basic(mock_request, common_tree): admin = get_item_admin() admin.tree = common_tree[''] form = admin.get_form(mock_request()) known_url_names = form.known_url_names assert set(known_url_names) == {'contacts_china', 'contacts_australia'} @pytest.mark.django_db def test_admin_tree_item_move(mock_request, common_tree): from sitetree.models import TreeItem, Tree main_tree = Tree(alias='main') main_tree.save() 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 = get_item_admin() admin.item_move(None, None, new_item_2.id, 'up') assert TreeItem.objects.get(pk=new_item_1.id).sort_order == 2 assert TreeItem.objects.get(pk=new_item_2.id).sort_order == 1 assert TreeItem.objects.get(pk=new_item_3.id).sort_order == 3 admin.item_move(None, None, new_item_1.id, 'down') assert TreeItem.objects.get(pk=new_item_1.id).sort_order == 3 assert TreeItem.objects.get(pk=new_item_2.id).sort_order == 1 assert TreeItem.objects.get(pk=new_item_3.id).sort_order == 2 @pytest.mark.django_db def test_admin_tree_item_get_tree(mock_request, common_tree): home = common_tree[''] tree = home.tree admin = get_item_admin() assert admin.get_tree(mock_request(), tree.pk) == tree assert admin.get_tree(mock_request(), None, home.pk) == tree @pytest.mark.django_db def test_admin_tree_item_save_model(mock_request, common_tree): users = common_tree['/users/'] tree = users.tree admin = get_item_admin() # Simulate bogus admin.previous_parent = users.parent users.parent = users admin.tree = tree admin.save_model(mock_request(), users, None, change=True) assert users.tree == admin.tree assert users.parent == admin.previous_parent @pytest.mark.django_db def test_admin_tree(): from sitetree.admin import TreeAdmin from sitetree.models import Tree admin = TreeAdmin(Tree, site) urls = admin.get_urls() assert len(urls) > 0 def test_redirects_handler(mock_request): from sitetree.admin import redirects_handler def get_handler(referer, item_id=None): req = mock_request(path=referer, meta={ '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('/') assert handler._headers['location'][1] == '/../' handler = get_handler('/delete/') assert handler._headers['location'][1] == '/delete/../../' handler = get_handler('/history/') assert handler._headers['location'][1] == '/history/../../' handler = get_handler('/history/', 42) assert handler._headers['location'][1] == '/history/../' django-sitetree-1.9.0/sitetree/tests/test_dynamic.py000066400000000000000000000062021321511234300226230ustar00rootroot00000000000000#! -*- encoding: utf-8 -*- from __future__ import unicode_literals import pytest from .common import strip_tags @pytest.mark.django_db def test_dynamic_basic(render_template_tag, mock_template_context): from sitetree.toolbox import compose_dynamic_tree, register_dynamic_trees, tree, item, get_dynamic_trees from sitetree.sitetreeapp import _IDX_ORPHAN_TREES trees = [ compose_dynamic_tree([tree('dynamic1', items=[ item('dynamic1_1', '/dynamic1_1_url', url_as_pattern=False, sort_order=2), item('dynamic1_2', '/dynamic1_2_url', url_as_pattern=False, sort_order=1), ])]), compose_dynamic_tree([tree('dynamic2', items=[ item('dynamic2_1', '/dynamic2_1_url', url_as_pattern=False), item('dynamic2_2', '/dynamic2_2_url', url_as_pattern=False), ])]), ] register_dynamic_trees(*trees, reset_cache=True) # new less-brackets style result = strip_tags(render_template_tag('sitetree', 'sitetree_tree from "dynamic1"', mock_template_context())) assert 'dynamic1_1|dynamic1_2' in result assert 'dynamic2_1' not in result register_dynamic_trees(trees) result = strip_tags(render_template_tag('sitetree', 'sitetree_tree from "dynamic1"', mock_template_context())) assert 'dynamic1_1|dynamic1_2' in result assert 'dynamic2_1' not in result trees = get_dynamic_trees() assert len(trees[_IDX_ORPHAN_TREES]) == 2 from sitetree.sitetreeapp import _DYNAMIC_TREES _DYNAMIC_TREES.clear() @pytest.mark.django_db def test_dynamic_attach(render_template_tag, mock_template_context, common_tree): from sitetree.toolbox import compose_dynamic_tree, register_dynamic_trees, tree, item register_dynamic_trees([ compose_dynamic_tree([tree('dynamic1', items=[ item('dynamic1_1', '/dynamic1_1_url', url_as_pattern=False), item('dynamic1_2', '/dynamic1_2_url', url_as_pattern=False), ])], target_tree_alias='mytree'), compose_dynamic_tree([tree('dynamic2', items=[ item('dynamic2_1', '/dynamic2_1_url', url_as_pattern=False), item('dynamic2_2', '/dynamic2_2_url', url_as_pattern=False), ])], target_tree_alias='mytree', parent_tree_item_alias='ruweb'), ]) result = strip_tags(render_template_tag('sitetree', 'sitetree_tree from "mytree"', mock_template_context())) assert 'Web|dynamic2_1|dynamic2_2' in result assert 'China|dynamic1_1|dynamic1_2' in result from sitetree.sitetreeapp import _DYNAMIC_TREES _DYNAMIC_TREES.clear() @pytest.mark.django_db def test_dynamic_attach_from_module(render_template_tag, mock_template_context, settings): from sitetree.toolbox import compose_dynamic_tree, register_dynamic_trees register_dynamic_trees(compose_dynamic_tree('sitetree.tests', include_trees=['dynamic4'])) result = strip_tags(render_template_tag('sitetree', 'sitetree_tree from "dynamic4"', mock_template_context())) assert 'dynamic4_1' in result settings.DEBUG = True with pytest.warns(UserWarning): compose_dynamic_tree('nonexistent') from sitetree.sitetreeapp import _DYNAMIC_TREES _DYNAMIC_TREES.clear() django-sitetree-1.9.0/sitetree/tests/test_forms.py000066400000000000000000000021161321511234300223250ustar00rootroot00000000000000#! -*- encoding: utf-8 -*- from __future__ import unicode_literals import pytest @pytest.mark.django_db def test_form(common_tree): from sitetree.toolbox import TreeItemForm form = TreeItemForm(tree='mytree', tree_item='root') items_field = form.fields['tree_item'] assert items_field.tree == 'mytree' assert items_field.initial == 'root' assert len(items_field.choices) == len(common_tree) @pytest.mark.django_db def test_field(common_tree): from sitetree.toolbox import TreeItemChoiceField len_common_tree = len(common_tree) items_field = TreeItemChoiceField('mytree', initial='root') assert items_field.tree == 'mytree' assert items_field.initial == 'root' assert len(items_field.choices) == len_common_tree home_item = common_tree[''] item = items_field.clean(home_item.id) assert item == home_item assert items_field.clean('') is None assert items_field.clean(len_common_tree + 100) is None # Unknown id items_field = TreeItemChoiceField(home_item.tree, initial='root') assert items_field.tree == 'mytree' django-sitetree-1.9.0/sitetree/tests/test_management.py000066400000000000000000000056761321511234300233310ustar00rootroot00000000000000#! -*- encoding: utf-8 -*- from __future__ import unicode_literals from json import loads import pytest from django.core.management import call_command from django.core.management.base import CommandError from django.core.serializers.base import DeserializationError @pytest.mark.django_db def test_sitetreeload(tmpdir, capsys): from sitetree.models import Tree, TreeItem def load(treedump, command_kwargs=None): f = tmpdir.join('somefile.json') f.write(treedump) command_kwargs = command_kwargs or {} call_command('sitetreeload', '%s' % f, **command_kwargs) treedump = ( '[' '{"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"},' '{"pk": 8, "model": "sitetree.treeitem", ' '"fields": {"title": "tree item two", "alias": null, "url": "/", "tree": 2, "sort_order": 8, "parent": 7}}' ']' ) with pytest.raises(CommandError): load(treedump, dict(items_into_tree='nonexisting')) load(treedump) assert Tree.objects.filter(title='tree one').exists() assert Tree.objects.filter(title='tree two').exists() assert TreeItem.objects.get(title='tree item one', tree__alias='tree1') assert TreeItem.objects.get(title='tree item two', tree__alias='tree1', parent__title='tree item one') load(treedump, dict(items_into_tree='tree2')) assert TreeItem.objects.filter(title='tree item one', tree__alias='tree2').exists() load(treedump, dict(mode='replace')) assert TreeItem.objects.filter(title='tree item one').count() == 1 with pytest.raises(DeserializationError): load(treedump.replace('7}}', '7}},'), dict(mode='replace')) load(treedump.replace('7}}', '27}}'), dict(mode='replace')) out, err = capsys.readouterr() assert 'does not exist.' in err @pytest.mark.django_db def test_sitetreedump(capsys, common_tree): call_command('sitetreedump') out, _ = capsys.readouterr() out = loads(out) assert len(out) == len(common_tree) call_command('sitetreedump', 'notree') out, _ = capsys.readouterr() out = loads(out) assert out == [] @pytest.mark.django_db def test_sitetreedump(capsys): from sitetree.models import TreeItem call_command('sitetree_resync_apps', 'sitetree.tests') out, _ = capsys.readouterr() assert 'Sitetrees found in' in out assert len(TreeItem.objects.all()) == 2 django-sitetree-1.9.0/sitetree/tests/test_models.py000066400000000000000000000022261321511234300224640ustar00rootroot00000000000000#! -*- encoding: utf-8 -*- from __future__ import unicode_literals import pytest @pytest.mark.django_db def test_model_tree(): from sitetree.models import Tree tree = Tree(alias='test') tree.save() assert str(tree) == tree.alias assert tree.get_title() == tree.alias with pytest.raises(Exception): Tree(alias='test').save() # Unique alias @pytest.mark.django_db def test_model_tree_item(): from sitetree.models import Tree, TreeItem tree1 = Tree(alias='test') tree1.save() item1 = TreeItem(tree=tree1, alias='only', title='only title') item1.save() assert str(item1) == item1.title item2 = TreeItem(tree=tree1, alias='other', parent=item1) item2.save() item3 = TreeItem(tree=tree1, parent=item1) item3.save() item3.sort_order = 100 item3.parent = item3 item3.save() assert item3.parent is None # Can't be itself assert item3.sort_order == 100 item3.sort_order = 0 item3.save() assert item3.sort_order == item3.id # Automatic ordering with pytest.raises(Exception): TreeItem(tree=tree1, alias='only').save() # Unique alias within tree django-sitetree-1.9.0/sitetree/tests/test_other.py000066400000000000000000000050231321511234300223200ustar00rootroot00000000000000#! -*- encoding: utf-8 -*- from __future__ import unicode_literals import pytest from sitetree.settings import ALIAS_TRUNK from .common import strip_tags @pytest.mark.django_db def test_stress(render_template_tag, mock_template_context, build_tree, common_tree): build_tree( {'alias': 'othertree'}, [{'title': 'Root', 'url': '/', 'children': [ {'title': 'Other title', 'url': '/contacts/russia/web/private/'}, {'title': 'Title_{{ myvar }}', 'url': '/some/'} ]}], ) context = mock_template_context(context_dict={'myvar': 'myval'}, request_path='/contacts/russia/web/private/') title = render_template_tag('sitetree', 'sitetree_page_title from "mytree"', context) title_other = render_template_tag('sitetree', 'sitetree_page_title from "othertree"', context) hint = render_template_tag('sitetree', 'sitetree_page_hint from "mytree"', context) description = render_template_tag('sitetree', 'sitetree_page_description from "mytree"', context) tree = strip_tags(render_template_tag('sitetree', 'sitetree_tree from "mytree"', context)) breadcrumbs = strip_tags(render_template_tag('sitetree', 'sitetree_breadcrumbs from "mytree"', context)) menu = render_template_tag('sitetree', 'sitetree_menu from "mytree" include "%s"' % ALIAS_TRUNK, context) menu_other = render_template_tag('sitetree', 'sitetree_menu from "othertree" include "%s"' % ALIAS_TRUNK, context) assert title == 'Private' assert title_other == 'Other title' assert hint == 'Private Area Hint' assert description == 'Private Area Description' assert breadcrumbs == 'Home|>|Russia|>|Web|>|Private' assert strip_tags(menu) == 'Home|Users|Moderators|Ordinary|Articles|About cats|Good|Bad|Ugly|About dogs|' \ 'Contacts|Russia|Web|Public|Private|Postal|Australia|Darwin|China' assert 'current_item current_branch">Private' in menu assert strip_tags(menu_other) == 'Root|Other title|Title_myval' assert 'current_item current_branch">Other title' in menu_other assert tree == 'Home|Users|Moderators|Ordinary|Articles|About cats|Good|Bad|Ugly|About dogs|About mice|Contacts|' \ 'Russia|Web|Public|Private|Australia|Darwin|China' def test_lazy_title(mock_template_context): from sitetree.sitetreeapp import LazyTitle, get_sitetree assert LazyTitle('one') == 'one' title = LazyTitle('here{% no_way %}there') get_sitetree().current_page_context = mock_template_context() assert title == 'herethere' django-sitetree-1.9.0/sitetree/tests/test_templatetags.py000066400000000000000000000326101321511234300236730ustar00rootroot00000000000000#! -*- encoding: utf-8 -*- from __future__ import unicode_literals import pytest from django.template.base import TemplateSyntaxError from django.utils.translation import activate, deactivate_all from sitetree.exceptions import SiteTreeError from sitetree.settings import ALIAS_THIS_ANCESTOR_CHILDREN, ALIAS_THIS_CHILDREN, ALIAS_THIS_PARENT_SIBLINGS, \ ALIAS_THIS_SIBLINGS, ALIAS_TRUNK @pytest.mark.django_db def test_items_hook(render_template_tag, mock_template_context, common_tree): from sitetree.toolbox import register_items_hook def my_processor(tree_items, tree_sender): for item in tree_items: item.hint = 'hooked_hint_%s' % item.title return tree_items register_items_hook(my_processor) result = render_template_tag('sitetree', 'sitetree_tree from "mytree"', mock_template_context()) assert 'hooked_hint_Darwin' in result assert 'hooked_hint_Australia' in result assert 'hooked_hint_China' in result register_items_hook(None) # Reset. @pytest.mark.django_db def test_i18n(build_tree, render_template_tag, mock_template_context): from sitetree.toolbox import register_i18n_trees build_tree( {'alias': 'i18tree'}, [{'title': 'My title', 'url': '/url_default/'}], ) build_tree( {'alias': 'i18tree_ru'}, [{'title': 'Заголовок', 'url': '/url_ru/'}], ) build_tree( {'alias': 'i18tree_pt-br'}, [{'title': 'Meu Título', 'url': '/url_pt-br/'}], ) build_tree( {'alias': 'i18tree_zh-hans'}, [{'title': '我蒂特', 'url': '/url_zh-hans/'}], ) register_i18n_trees(['i18tree']) activate('en') result = render_template_tag('sitetree', 'sitetree_tree from "i18tree"', mock_template_context()) assert '/url_default/' in result assert 'My title' in result activate('ru') result = render_template_tag('sitetree', 'sitetree_tree from "i18tree"', mock_template_context()) assert '/url_ru/' in result assert 'Заголовок' in result activate('pt-br') result = render_template_tag('sitetree', 'sitetree_tree from "i18tree"', mock_template_context()) assert '/url_pt-br/' in result assert 'Meu Título' in result activate('zh-hans') result = render_template_tag('sitetree', 'sitetree_tree from "i18tree"', mock_template_context()) assert '/url_zh-hans/' in result assert '我蒂特' in result deactivate_all() @pytest.mark.django_db def test_restricted(render_template_tag, mock_template_context, common_tree): context = mock_template_context() result = render_template_tag('sitetree', 'sitetree_tree from "mytree"', context) assert '"/contacts/australia/darwin/"' in result assert '"/contacts/australia/alice/"' not in result context = mock_template_context(user_data='some') result = render_template_tag('sitetree', 'sitetree_tree from "mytree"', context) assert '"/contacts/australia/darwin/"' not in result assert '"/contacts/australia/alice/"' in result @pytest.mark.django_db def test_permissions(admin_user, build_tree, render_template_tag, mock_template_context): from sitetree.models import TreeItem build_tree( {'alias': 'restricted_tree'}, [ {'title': 'Minjilang', 'access_restricted': True, 'url': '/contacts/australia/minjilang/'}, {'title': 'Broome', 'access_restricted': True, 'access_perm_type': TreeItem.PERM_TYPE_ANY, 'access_permissions': ['add_group', 'add_tree'], 'url': '/contacts/australia/broome/'}, {'title': 'Karratha', 'access_restricted': True, 'access_perm_type': TreeItem.PERM_TYPE_ALL, 'access_permissions': ['add_group', 'add_tree'], 'url': '/contacts/australia/karratha/'}, ], ) context = mock_template_context(user_data=admin_user) result = render_template_tag('sitetree', 'sitetree_tree from "restricted_tree"', context) assert '"/contacts/australia/broome/"' in result assert '"/contacts/australia/karratha/"' in result assert '"/contacts/australia/minjilang/"' not in result admin_user._perm_cache.remove('sitetree.add_tree') context = mock_template_context(user_data=admin_user) result = render_template_tag('sitetree', 'sitetree_tree from "restricted_tree"', context) assert '"/contacts/australia/broome/"' in result assert '"/contacts/australia/karratha/"' not in result assert '"/contacts/australia/minjilang/"' not in result @pytest.mark.django_db def test_title_vars(render_template_tag, mock_template_context, common_tree): context = mock_template_context({'subtitle': 'title_from_var'}) result = render_template_tag('sitetree', 'sitetree_tree from "mytree"', context) assert 'Public title_from_var' in result @pytest.mark.django_db def test_urlpattern_resolve(monkeypatch, render_template_tag, mock_template_context, common_tree): monkeypatch.setattr('sitetree.sitetreeapp.UNRESOLVED_ITEM_MARKER', 'UNKNOWN') context = mock_template_context() result = render_template_tag('sitetree', 'sitetree_tree from "mytree"', context) assert '"/contacts/australia/australia_var/"' in result assert 'href="UNKNOWN" >China' in result context = mock_template_context({'australia_var': 33}) result = render_template_tag('sitetree', 'sitetree_tree from "mytree"', context) assert 'href="/contacts/australia/33/"' in result context = mock_template_context({'australia_var': 'пробапера'}) # non-ascii result = render_template_tag('sitetree', 'sitetree_tree from "mytree"', context) assert '"/contacts/australia/australia_var/"' in result @pytest.mark.django_db def test_sitetree_tree(render_template_tag, mock_template_context, common_tree): context = mock_template_context() with pytest.raises(TemplateSyntaxError): render_template_tag('sitetree', 'sitetree_tree "mytree"', context) assert render_template_tag('sitetree', 'sitetree_tree from "notree"', context) == '\n' result = render_template_tag('sitetree', 'sitetree_tree from "mytree"', context) assert '"/articles/cats/ugly/"' in result assert '"/contacts/russia/postal/"' not in result # insitetree False assert '"/users/ordinary/"' in result assert '"/users/hidden/"' not in result @pytest.mark.django_db def test_sitetree_children(render_template_tag, mock_template_context, common_tree): context = mock_template_context({ 'parent_item': common_tree['/users/'] }) with pytest.raises(TemplateSyntaxError): render_template_tag('sitetree', 'sitetree_children', context) result = render_template_tag( 'sitetree', 'sitetree_children of parent_item for sitetree template "sitetree/tree.html"', context) assert '"/users/moderators/"' in result assert '"/users/"' not in result assert '"/users/hidden/"' not in result @pytest.mark.django_db def test_sitetree_breadcrumbs(render_template_tag, mock_template_context, common_tree): result = render_template_tag('sitetree', 'sitetree_breadcrumbs from "notree"', mock_template_context()) # non-existing tree assert result.strip() == '
      \n\t\n
    ' with pytest.raises(TemplateSyntaxError): render_template_tag('sitetree', 'sitetree_breadcrumbs') result = render_template_tag('sitetree', 'sitetree_breadcrumbs from "mytree"', mock_template_context()) assert result.strip() == '
      \n\t\n
    ' context = mock_template_context(request_path='/contacts/russia/web/private/') result = render_template_tag('sitetree', 'sitetree_breadcrumbs from "mytree"', context) assert '"/contacts/russia/web/"' in result assert '"/contacts/russia/"' in result assert '"/contacts/"' not in result # inbreadcrumbs False assert '"/home/"' in result def check_page_attr_tag(realm, value, settings, monkeypatch, render_template_tag, mock_template_context): with pytest.raises(TemplateSyntaxError): # Invalid tag arguments. render_template_tag('sitetree', 'sitetree_page_%s' % realm) context = mock_template_context(request_path='/contacts/russia/') result = render_template_tag('sitetree', 'sitetree_page_%s from "mytree"' % realm, context) assert result == value result = render_template_tag('sitetree', 'sitetree_page_%s from "mytree" as somevar' % realm, context) assert result == '' assert context.get('somevar') == value settings.DEBUG = True with pytest.raises(SiteTreeError) as e: render_template_tag('sitetree', 'sitetree_page_%s from "mytree"' % realm) assert 'django.core.context_processors.request' in '%s' % e.value context = mock_template_context(request_path='/unknown_url/') with pytest.raises(SiteTreeError) as e: render_template_tag('sitetree', 'sitetree_page_%s from "mytree"' % realm, context) assert 'Unable to resolve current sitetree item' in '%s' % e.value monkeypatch.setattr('sitetree.sitetreeapp.RAISE_ITEMS_ERRORS_ON_DEBUG', False) result = render_template_tag('sitetree', 'sitetree_page_%s from "mytree"' % realm, context) assert result == '' @pytest.mark.django_db def test_sitetree_page_title(settings, monkeypatch, render_template_tag, mock_template_context, common_tree): check_page_attr_tag('title', 'Russia', settings, monkeypatch, render_template_tag, mock_template_context) @pytest.mark.django_db def test_sitetree_page_hint(settings, monkeypatch, render_template_tag, mock_template_context, common_tree): check_page_attr_tag('hint', 'The place', settings, monkeypatch, render_template_tag, mock_template_context) @pytest.mark.django_db def test_sitetree_page_description(settings, monkeypatch, render_template_tag, mock_template_context, common_tree): check_page_attr_tag( 'description', 'Russian Federation', settings, monkeypatch, render_template_tag, mock_template_context) @pytest.mark.django_db def test_sitetree_url(render_template_tag, mock_template_context, common_tree): with pytest.raises(TemplateSyntaxError): render_template_tag('sitetree', 'sitetree_url') target_url = '/contacts/russia/' tree_item = common_tree[target_url] context = mock_template_context({'item_var': tree_item}) result = render_template_tag('sitetree', 'sitetree_url for item_var', context) assert result == target_url result = render_template_tag('sitetree', 'sitetree_url for item_var as somevar', context) assert result == '' assert context.get('somevar') == target_url @pytest.mark.django_db def test_sitetree_menu(render_template_tag, mock_template_context, common_tree): result = render_template_tag( 'sitetree', 'sitetree_menu from "notree" include "%s"' % ALIAS_TRUNK, mock_template_context()) # non-existing tree assert result.strip() == '
      \n\t\n
    ' with pytest.raises(TemplateSyntaxError): render_template_tag('sitetree', 'sitetree_menu') item_ruweb = common_tree['/contacts/russia/web/'] context = mock_template_context(request_path='/') result = render_template_tag('sitetree', 'sitetree_menu from "mytree" include "%s"' % item_ruweb.alias, context) assert '"/contacts/russia/web/"' not in result assert '"/contacts/russia/web/public/"' in result assert '"/contacts/russia/web/private/"' in result result = render_template_tag('sitetree', 'sitetree_menu from "mytree" include "%s"' % item_ruweb.id, context) assert '"/contacts/russia/web/"' not in result assert '"/contacts/russia/web/public/"' in result assert '"/contacts/russia/web/private/"' in result context = mock_template_context(request_path='/contacts/russia/web/') result = render_template_tag('sitetree', 'sitetree_menu from "mytree" include "%s"' % ALIAS_TRUNK, context) assert '"/users/moderators/"' in result assert '"/articles/cats/ugly/"' in result assert '"/home/"' in result assert '"/users/ordinary/"' in result assert '"/users/hidden/"' not in result assert 'class="current_item current_branch">Web' in result assert 'class="current_branch">Contacts' in result assert 'class="current_branch">Home' in result context = mock_template_context(request_path='/articles/') result = render_template_tag('sitetree', 'sitetree_menu from "mytree" include "%s"' % ALIAS_THIS_CHILDREN, context) assert '"/articles/"' not in result assert '"/articles/cats/"' in result assert '"/articles/cats/ugly/"' in result assert '"/articles/mice/"' not in result # inmenu False context = mock_template_context(request_path='/articles/cats/bad/') result = render_template_tag( 'sitetree', 'sitetree_menu from "mytree" include "%s"' % ALIAS_THIS_SIBLINGS, context) assert '"/articles/cats/"' not in result assert '"/articles/cats/good/"' in result assert '"/articles/cats/bad/"' in result assert '"/articles/cats/ugly/"' in result context = mock_template_context(request_path='/contacts/russia/web/public/') result = render_template_tag( 'sitetree', 'sitetree_menu from "mytree" include "%s"' % ALIAS_THIS_PARENT_SIBLINGS, context) assert '"/contacts/russia/"' not in result assert '"/contacts/russia/web/"' in result assert '"/contacts/russia/web/public/"' in result assert '"/contacts/russia/postal/"' in result context = mock_template_context(request_path='/contacts/russia/web/public/') result = render_template_tag( 'sitetree', 'sitetree_menu from "mytree" include "%s"' % ALIAS_THIS_ANCESTOR_CHILDREN, context) assert '"/home/"' not in result assert '"/contacts/russia/web/public/"' in result django-sitetree-1.9.0/sitetree/tests/test_utils.py000066400000000000000000000062571321511234300223510ustar00rootroot00000000000000#! -*- encoding: utf-8 -*- from __future__ import unicode_literals import pytest from django.core.exceptions import ImproperlyConfigured def test_import(): from sitetree.utils import import_project_sitetree_modules modules = import_project_sitetree_modules() assert len(modules) == 1 assert modules[0].sitetrees def test_get_app_n_model(): from sitetree.utils import get_app_n_model app, model = get_app_n_model('MODEL_TREE') assert app == 'sitetree' assert model == 'Tree' with pytest.raises(ImproperlyConfigured): get_app_n_model('ALIAS_TRUNK') def test_import_app_sitetree_module(): from sitetree.utils import import_app_sitetree_module with pytest.raises(ImportError): import_app_sitetree_module('sitetre') def test_import_project_sitetree_modules(): from sitetree import settings from sitetree.models import Tree from sitetree.utils import get_model_class cls = get_model_class('MODEL_TREE') assert cls is Tree settings.MODEL_TREE = 'nowhere.Model' with pytest.raises(ImproperlyConfigured): get_model_class('MODEL_TREE') def get_permission_and_name(): from django.contrib.auth.models import Permission perm = Permission.objects.all()[0] perm_name = '%s.%s' % (perm.content_type.app_label, perm.codename) return perm, perm_name class TestPermissions(): def test_permission_any(self): from sitetree.toolbox import item i1 = item('root', 'url') assert i1.access_perm_type == i1.PERM_TYPE_ALL assert i1.permissions == [] i2 = item('root', 'url', perms_mode_all=True) assert i2.access_perm_type == i1.PERM_TYPE_ALL i3 = item('root', 'url', perms_mode_all=False) assert i3.access_perm_type == i1.PERM_TYPE_ANY def test_int_permissions(self): from sitetree.toolbox import item i1 = item('root', 'url', access_by_perms=[1, 2, 3]) assert i1.permissions == [1, 2, 3] @pytest.mark.django_db def test_valid_string_permissions(self): from sitetree.toolbox import item perm, perm_name = get_permission_and_name() i1 = item('root', 'url', access_by_perms=perm_name) assert i1.permissions == [perm] @pytest.mark.django_db def test_perm_obj_permissions(self): from sitetree.toolbox import item perm, __ = get_permission_and_name() i1 = item('root', 'url', access_by_perms=perm) assert i1.permissions == [perm] @pytest.mark.django_db def test_bad_string_permissions(self): from sitetree.toolbox import item with pytest.raises(ValueError): item('root', 'url', access_by_perms='bad name') with pytest.raises(ValueError): item('root', 'url', access_by_perms='unknown.name') with pytest.raises(ValueError): item('root', 'url', access_by_perms=42.2) def test_access_restricted(self): from sitetree.toolbox import item # Test that default is False i0 = item('root', 'url', access_by_perms=1) assert i0.access_restricted # True is respected i1 = item('root', 'url') assert not i1.access_restricted django-sitetree-1.9.0/sitetree/tests/urls.py000066400000000000000000000006321321511234300211260ustar00rootroot00000000000000from django import VERSION from django.conf.urls import url urlpatterns = [ url(r'contacts/australia/(?P[^/]+)/', lambda r, value: None, name='contacts_australia'), url(r'contacts/australia/(?P\d+)/', lambda r, value: None, name='contacts_china'), ] if VERSION < (1, 10): from django.conf.urls import patterns urlpatterns.insert(0, '') urlpatterns = patterns(*urlpatterns) django-sitetree-1.9.0/sitetree/toolbox.py000066400000000000000000000006021321511234300204620ustar00rootroot00000000000000# Unused imports below are exposed as API. from .utils import get_tree_item_model, get_tree_model, tree, item, import_app_sitetree_module, \ import_project_sitetree_modules from .sitetreeapp import register_i18n_trees, register_items_hook, compose_dynamic_tree, register_dynamic_trees, \ get_dynamic_trees from .fields import TreeItemChoiceField from .forms import TreeItemForm django-sitetree-1.9.0/sitetree/utils.py000066400000000000000000000154131321511234300201420ustar00rootroot00000000000000from importlib import import_module from django.apps import apps from 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 apps_get_model = apps.get_model 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. :param str|unicode alias: :param str|unicode title: :param iterable items: dynamic sitetree items objects created by `item` function. :rtype: TreeBase """ 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|unicode title: :param str|unicode 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|unicode hint: hints are usually shown to users :param str|unicode alias: item name to address it from templates :param str|unicode 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||unicode|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. :rtype: TreeItemBase """ 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. :param str|unicode app: Application name :return: module|None """ 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). Returns a list of submodules. :rtype: list """ 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. :param str|unicode settings_entry_name: :rtype: tuple """ 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. :param str|unicode settings_entry_name: :rtype: TreeItemBase|TreeBase """ app_name, model_name = get_app_n_model(settings_entry_name) 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. :rtype: TreeBase """ return get_model_class('MODEL_TREE') def get_tree_item_model(): """Returns the TreeItem model, set for the project. :rtype: TreeItemBase """ return get_model_class('MODEL_TREE_ITEM') django-sitetree-1.9.0/tox.ini000066400000000000000000000007121321511234300161130ustar00rootroot00000000000000[tox] install_command = pip install {opts} {packages} skip_missing_interpreters = True envlist = py{27,34}-django{17,18,19,110,111} py{35,36}-django{18,19,110,111,20} [testenv] commands = python setup.py test deps = pytest pytest-django mock django17: Django>=1.7,<1.8 django18: Django>=1.8,<1.9 django19: Django>=1.9,<1.10 django110: Django>=1.10,<1.11 django111: Django>=1.11,<1.12 django20: Django>=2.0,<2.1