diff --git a/.gitignore b/.gitignore index 890183617..bd904efc9 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ /MANIFEST /wagtail.egg-info/ /docs/_build/ +/.tox/ diff --git a/.travis.yml b/.travis.yml index e23794d4f..cc1cb74e8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,7 +13,7 @@ services: install: - python setup.py install - pip install psycopg2 pyelasticsearch elasticutils==0.8.2 wand - - pip install coveralls + - pip install coveralls unittest2 # Pre-test configuration before_script: - psql -c 'create database wagtaildemo;' -U postgres diff --git a/CHANGELOG.txt b/CHANGELOG.txt index d1e313295..a0024b6a0 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -13,12 +13,17 @@ Changelog * MultiFieldPanel definitions now accept a 'classname' attribute, including a special classname of 'collapsible' to allow showing / hiding them on click * Added 'insert_editor_css' and 'insert_editor_js' hooks for passing in custom CSS / JS to the editor interface * Made JPEG compression level configurable through the IMAGE_COMPRESSION_QUALITY setting, and increased default to 85 + * Added document_served signal which gets fired when a document is downloaded * Added translation for Portuguese Brazil + * Made compatible with Python 2.6 + * 'richtext' template filter now wraps output in
, to assist in styling + * Embeds now save author_name and provider_name if set by oEmbed provider * Fix: non-ASCII characters in image filenames are now converted into ASCII equivalents rather than becoming all underscores * Fix: paths to fonts and images within CSS are no longer hard-coded to /static/ * Fix: Localization files for the JQuery UI datepicker are stored locally and only imported when a localization is known to be available * Fix: Page slugs are now validated on page edit * Fix: Filter objects are cached to avoid a database hit every time an {% image %} tag is compiled + * Fix: Moving or changing a site root page no longer causes URLs for subpages to change to 'None' 0.2 (11.03.2014) ~~~~~~~~~~~~~~~~ diff --git a/CONTRIBUTORS.rst b/CONTRIBUTORS.rst index 0c4153a7d..38e89a8e1 100644 --- a/CONTRIBUTORS.rst +++ b/CONTRIBUTORS.rst @@ -24,6 +24,7 @@ Contributors * v1kku * Miguel Vieira * Ben Emery +* David Smith Translators =========== diff --git a/README.rst b/README.rst index 62d42aa83..9a1961b32 100644 --- a/README.rst +++ b/README.rst @@ -26,6 +26,8 @@ Wagtail is a Django content management system built originally for the `Royal Co * Fast out of the box. `Varnish `_-friendly if you need it * Tests! But not enough; we're working hard to improve this +It supports Django 1.6.2+ on Python 2.6 and 2.7. Django 1.7 and Python 3 support are in progress. + Find out more at `wagtail.io `_. Documentation is at `wagtail.readthedocs.org `_. Got a question? Ask it on our `Google Group `_. diff --git a/docs/index.rst b/docs/index.rst index 96ff94c23..dbfe91f33 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -3,6 +3,8 @@ Welcome to Wagtail's documentation Wagtail is a modern, flexible CMS, built on Django. +It supports Django 1.6.2+ on Python 2.6 and 2.7. Django 1.7 and Python 3 support are in progress. + .. toctree:: :maxdepth: 3 diff --git a/requirements-dev.txt b/requirements-dev.txt index 201f86ed8..008d24d2e 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,5 +1,7 @@ # Requirements essential for developing wagtail (not needed to run it) +unittest2==0.5.1 + # For coverage and PEP8 linting coverage==3.7.1 flake8==2.1.0 diff --git a/runtests.py b/runtests.py index 027d61158..ab7bc37c3 100755 --- a/runtests.py +++ b/runtests.py @@ -31,9 +31,9 @@ if not settings.configured: settings.configure( DATABASES={ 'default': { - 'ENGINE': 'django.db.backends.postgresql_psycopg2', + 'ENGINE': os.environ.get('DATABASE_ENGINE', 'django.db.backends.postgresql_psycopg2'), 'NAME': 'wagtaildemo', - 'USER': 'postgres', + 'USER': os.environ.get('DATABASE_USER', 'postgres'), } }, ROOT_URLCONF='wagtail.tests.urls', @@ -82,6 +82,16 @@ if not settings.configured: 'wagtail.wagtailredirects', 'wagtail.tests', ], + + # Using DatabaseCache to make sure that the cache is cleared between tests. + # This prevents false-positives in some wagtail core tests where we are + # changing the 'wagtail_root_paths' key which may cause future tests to fail. + CACHES = { + 'default': { + 'BACKEND': 'django.core.cache.backends.db.DatabaseCache', + 'LOCATION': 'cache', + } + }, PASSWORD_HASHERS=( 'django.contrib.auth.hashers.MD5PasswordHasher', # don't use the intentionally slow default password hasher ), diff --git a/setup.py b/setup.py index eee178ed2..e8da542bc 100644 --- a/setup.py +++ b/setup.py @@ -34,6 +34,8 @@ setup( 'License :: OSI Approved :: BSD License', 'Operating System :: OS Independent', 'Programming Language :: Python', + 'Programming Language :: Python :: 2', + 'Programming Language :: Python :: 2.6', 'Programming Language :: Python :: 2.7', 'Framework :: Django', 'Topic :: Internet :: WWW/HTTP :: Site Management', @@ -44,7 +46,8 @@ setup( "django-compressor>=1.3", "django-libsass>=0.1", "django-modelcluster>=0.1", - "django-taggit>=0.11.2", + "django-taggit==0.11.2", + "django-treebeard==2.0", "Pillow>=2.3.0", "beautifulsoup4>=4.3.2", "lxml>=3.3.0", diff --git a/tox.ini b/tox.ini new file mode 100644 index 000000000..329c1c407 --- /dev/null +++ b/tox.ini @@ -0,0 +1,70 @@ +[deps] +dj16= + Django>=1.6,<1.7 + pyelasticsearch==0.6.1 + elasticutils==0.8.2 + unittest2 + +[tox] +envlist = + py26-dj16-postgres, + py26-dj16-sqlite, + py27-dj16-postgres, + py27-dj16-sqlite + +# mysql not currently supported +# (wagtail.wagtailimages.tests.TestImageEditView currently fails with a +# foreign key constraint error) +# py26-dj16-mysql +# py27-dj16-mysql + +[testenv] +commands=./runtests.py + +[testenv:py26-dj16-postgres] +basepython=python2.6 +deps = + {[deps]dj16} + psycopg2==2.5.2 +setenv = + DATABASE_ENGINE=django.db.backends.postgresql_psycopg2 + +[testenv:py26-dj16-sqlite] +basepython=python2.6 +deps = + {[deps]dj16} +setenv = + DATABASE_ENGINE=django.db.backends.sqlite3 + +[testenv:py26-dj16-mysql] +basepython=python2.6 +deps = + {[deps]dj16} + MySQL-python==1.2.5 +setenv = + DATABASE_ENGINE=django.db.backends.mysql + DATABASE_USER=wagtail + +[testenv:py27-dj16-postgres] +basepython=python2.7 +deps = + {[deps]dj16} + psycopg2==2.5.2 +setenv = + DATABASE_ENGINE=django.db.backends.postgresql_psycopg2 + +[testenv:py27-dj16-sqlite] +basepython=python2.7 +deps = + {[deps]dj16} +setenv = + DATABASE_ENGINE=django.db.backends.sqlite3 + +[testenv:py27-dj16-mysql] +basepython=python2.7 +deps = + {[deps]dj16} + MySQL-python==1.2.5 +setenv = + DATABASE_ENGINE=django.db.backends.mysql + DATABASE_USER=wagtail diff --git a/wagtail/tests/fixtures/test.json b/wagtail/tests/fixtures/test.json index 571a9df3f..0a0fc0528 100644 --- a/wagtail/tests/fixtures/test.json +++ b/wagtail/tests/fixtures/test.json @@ -141,6 +141,29 @@ } }, +{ + "pk": 7, + "model": "wagtailcore.page", + "fields": { + "title": "About us", + "numchild": 0, + "show_in_menus": true, + "live": true, + "depth": 3, + "content_type": ["tests", "simplepage"], + "path": "000100010002", + "url_path": "/home/about-us/", + "slug": "about-us" + } +}, +{ + "pk": 7, + "model": "tests.simplepage", + "fields": { + "content": "

We are really good.

" + } +}, + { "pk": 1, "model": "wagtailcore.site", diff --git a/wagtail/tests/urls.py b/wagtail/tests/urls.py index d133a4bf8..d04c871a3 100644 --- a/wagtail/tests/urls.py +++ b/wagtail/tests/urls.py @@ -2,14 +2,8 @@ from django.conf.urls import patterns, include, url from wagtail.wagtailcore import urls as wagtail_urls from wagtail.wagtailadmin import urls as wagtailadmin_urls -from wagtail.wagtailimages import urls as wagtailimages_urls -from wagtail.wagtailembeds import urls as wagtailembeds_urls -from wagtail.wagtaildocs import admin_urls as wagtaildocs_admin_urls from wagtail.wagtaildocs import urls as wagtaildocs_urls -from wagtail.wagtailsnippets import urls as wagtailsnippets_urls -from wagtail.wagtailsearch.urls import frontend as wagtailsearch_frontend_urls, admin as wagtailsearch_admin_urls -from wagtail.wagtailusers import urls as wagtailusers_urls -from wagtail.wagtailredirects import urls as wagtailredirects_urls +from wagtail.wagtailsearch.urls import frontend as wagtailsearch_frontend_urls # Signal handlers from wagtail.wagtailsearch import register_signal_handlers as wagtailsearch_register_signal_handlers @@ -17,16 +11,8 @@ wagtailsearch_register_signal_handlers() urlpatterns = patterns('', - url(r'^admin/images/', include(wagtailimages_urls)), - url(r'^admin/embeds/', include(wagtailembeds_urls)), - url(r'^admin/documents/', include(wagtaildocs_admin_urls)), - url(r'^admin/snippets/', include(wagtailsnippets_urls)), - url(r'^admin/search/', include(wagtailsearch_admin_urls)), - url(r'^admin/users/', include(wagtailusers_urls)), - url(r'^admin/redirects/', include(wagtailredirects_urls)), url(r'^admin/', include(wagtailadmin_urls)), url(r'^search/', include(wagtailsearch_frontend_urls)), - url(r'^documents/', include(wagtaildocs_urls)), # For anything not caught by a more specific rule above, hand over to diff --git a/wagtail/vendor/django-treebeard/.coveragerc b/wagtail/vendor/django-treebeard/.coveragerc deleted file mode 100644 index 6f2f2bdf9..000000000 --- a/wagtail/vendor/django-treebeard/.coveragerc +++ /dev/null @@ -1,17 +0,0 @@ -[run] -branch = True -source = treebeard -parallel = True - -[paths] -source = - ./ - *\workspace\django-treebeard\tox_db\*\tox_django\*\tox_python\*\os\windows/ - */jobs/django-treebeard/workspace/TOX_DB/*/TOX_DJANGO/*/TOX_PYTHON/*/os/osx/ - */workspace/django-treebeard/TOX_DB/*/TOX_DJANGO/*/TOX_PYTHON/*/os/linux/ - -[report] -omit = - */tests/* - */numconv.py -precision = 2 diff --git a/wagtail/vendor/django-treebeard/.hgignore b/wagtail/vendor/django-treebeard/.hgignore deleted file mode 100644 index cda07805f..000000000 --- a/wagtail/vendor/django-treebeard/.hgignore +++ /dev/null @@ -1,20 +0,0 @@ -syntax: glob - -.DS_Store -.buildinfo -*.pyc -*.orig -*.swp -.coverage -build -dist -_build -MANIFEST -.project -.pydevproject -.settings -htmlcov -.tox -*.xml -.coverage.* -*.egg-info diff --git a/wagtail/vendor/django-treebeard/.hgtags b/wagtail/vendor/django-treebeard/.hgtags deleted file mode 100644 index fa292fb36..000000000 --- a/wagtail/vendor/django-treebeard/.hgtags +++ /dev/null @@ -1,14 +0,0 @@ -29b76a1f6042e63bf9f234bb48c95dcfbd0afc8d 1.0 -5e39c474d8ea24993777332f8d7ccfd0da1014ad 1.1 -859f2a36845426d0ff8914cbc58a8b5c52f07256 1.5 -859f2a36845426d0ff8914cbc58a8b5c52f07256 1.5 -630024c53f5fac1f5aae412fcfc8c207e5a9d3da 1.5 -3fe083f135c7e36c08e76448368355af30125e50 1.51 -0ea8c876d30783ef3a0e8b6f9565371c0f13e8a5 1.52 -d73b1298ef049d6ddc5fbfc665f51a3c7b376494 1.6 -d73b1298ef049d6ddc5fbfc665f51a3c7b376494 1.6 -b510c7559b915a59f647276affd460e24c85ae9c 1.6 -0b95d619fc8a264ac93ed6de6b1e34886e7d5d07 1.60 -1af1b23d695d27f963d6393327f6a3c0bdd7df31 1.61 -23f4629de5d7df21f03fdf26abed3baea786ca8b 2.0b1 -87a28ab0b44063a2989d805c56483dacbb637532 2.0b2 diff --git a/wagtail/vendor/django-treebeard/AUTHORS b/wagtail/vendor/django-treebeard/AUTHORS deleted file mode 100644 index 680d9e5e9..000000000 --- a/wagtail/vendor/django-treebeard/AUTHORS +++ /dev/null @@ -1,19 +0,0 @@ -Treebeard was created in 2008 by Gustavo Picon. - -Contributions made by: - - * Aureal - * Jean-Matthieu Barbier - * Jesus del Carpio - * chembervint - * Matt Hoskins - * Rob Hudson - * Alexey Kinyov - * omad - * Oregon Center for Applied Science - * Alejandro Peralta - * Jaap Roes - * Alexei Vlasov - * moberley - * czare1 - * Fernando Gutierrez (xbito) diff --git a/wagtail/vendor/django-treebeard/CHANGES b/wagtail/vendor/django-treebeard/CHANGES deleted file mode 100644 index 9c0beebbe..000000000 --- a/wagtail/vendor/django-treebeard/CHANGES +++ /dev/null @@ -1,150 +0,0 @@ - -Release 2.0b2 (December, 2013) ------------------------------- - -* Dropped support for Python 2.5 - - -Release 2.0b1 (May 29, 2013) ----------------------------- - -This is a beta release. - -* Added support for Django 1.5 and Python 3.X -* Updated docs: the library supports python 2.5+ and Django 1.4+. Dropped - support for older versions -* Revamped admin interface for MP and NS trees, supporting drag&drop to reorder - nodes. Work on this patch was sponsored by the - `Oregon Center for Applied Science`_, inspired by `FeinCMS`_ developed by - `Jesús del Carpio`_ with tests from `Fernando Gutierrez`_. Thanks ORCAS! -* Updated setup.py to use distribute/setuptools instead of distutils -* Now using pytest for testing -* Small optimization to ns_tree.is_root -* Moved treebeard.tests to it's own directory (instead of tests.py) -* Added the runtests.py test runner -* Added tox support -* Fixed drag&drop bug in the admin -* Fixed a bug when moving MP_Nodes -* Using .pk instead of .id when accessing nodes. -* Removed the Benchmark (tbbench) and example (tbexample) apps. -* Fixed url parts join issues in the admin. -* Fixed: Now installing the static resources -* Fixed ManyToMany form field save handling -* In the admin, the node is now saved when moving so it can trigger handlers - and/or signals. -* Improved translation files, including javascript. -* Renamed Node.get_database_engine() to Node.get_database_vendor(). As the name - implies, it returns the database vendor instead of the engine used. Treebeard - will get the value from Django, but you can subclass the method if needed. - -Release 1.61 (Jul 24, 2010) ---------------------------- - -* Added admin i18n. Included translations: es, ru -* Fixed a bug when trying to introspect the database engine used in Django 1.2+ - while using new style db settings (DATABASES). Added - Node.get_database_engine to deal with this. - -Release 1.60 (Apr 18, 2010) ---------------------------- - -* Added get_annotated_list -* Complete revamp of the documentation. It's now divided in sections for easier - reading, and the package includes .rst files instead of the html build. -* Added raw id fields support in the admin -* Fixed setup.py to make it work in 2.4 again -* The correct ordering in NS/MP trees is now enforced in the queryset. -* Cleaned up code, removed some unnecessary statements. -* Tests refactoring, to make it easier to spot the model being tested. -* Fixed support of trees using proxied models. It was broken due to a bug in - Django. -* Fixed a bug in add_child when adding nodes to a non-leaf in sorted MP. -* There are now 648 unit tests. Test coverage is 96% -* This will be the last version compatible with Django 1.0. There will be a - a 1.6.X branch maintained for urgent bug fixes, but the main development will - focus on recent Django versions. - - -Release 1.52 (Dec 18, 2009) ---------------------------- - -* Really fixed the installation of templates. - - -Release 1.51 (Dec 16, 2009) ---------------------------- - -* Forgot to include treebeard/tempates/\*.html in MANIFEST.in - - -Release 1.5 (Dec 15, 2009) --------------------------- - -New features added -~~~~~~~~~~~~~~~~~~ - -* Forms - - - Added MoveNodeForm - -* Django Admin - - - Added TreeAdmin - -* MP_Node - - - Added 2 new checks in MP_Node.find_problems(): - - 4. a list of ids of nodes with the wrong depth value for - their path - 5. a list of ids nodes that report a wrong number of children - - - Added a new (safer and faster but less comprehensive) MP_Node.fix_tree() - approach. - -* Documentation - - - Added warnings in the documentation when subclassing MP_Node or NS_Node - and adding a new Meta. - - - HTML documentation is now included in the package. - - - CHANGES file and section in the docs. - -* Other changes: - - - script to build documentation - - - updated numconv.py - - -Bugs fixed -~~~~~~~~~~ - -* Added table quoting to all the sql queries that bypass the ORM. - Solves bug in postgres when the table isn't created by syncdb. - -* Removing unused method NS_Node._find_next_node - -* Fixed MP_Node.get_tree to include the given parent when given a leaf node - - -Release 1.1 (Nov 20, 2008) --------------------------- - -Bugs fixed -~~~~~~~~~~ - -* Added exceptions.py - - -Release 1.0 (Nov 19, 2008) --------------------------- - -* First public release. - - -.. _Oregon Center for Applied Science: http://www.orcasinc.com/ -.. _FeinCMS: http://www.feincms.org -.. _Jesús del Carpio: http://www.isgeek.net -.. _Fernando Gutierrez: http://xbito.pe diff --git a/wagtail/vendor/django-treebeard/LICENSE b/wagtail/vendor/django-treebeard/LICENSE deleted file mode 100644 index 6b0b1270f..000000000 --- a/wagtail/vendor/django-treebeard/LICENSE +++ /dev/null @@ -1,203 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - diff --git a/wagtail/vendor/django-treebeard/MANIFEST.in b/wagtail/vendor/django-treebeard/MANIFEST.in deleted file mode 100644 index fd87d7ac1..000000000 --- a/wagtail/vendor/django-treebeard/MANIFEST.in +++ /dev/null @@ -1,3 +0,0 @@ -include CHANGES LICENSE NOTICE README.rst UPDATING MANIFEST.in -recursive-include docs Makefile README.rst *.py *.rst -recursive-include treebeard *.py *.html *.js *.css *.png diff --git a/wagtail/vendor/django-treebeard/NOTICE b/wagtail/vendor/django-treebeard/NOTICE deleted file mode 100644 index e69de29bb..000000000 diff --git a/wagtail/vendor/django-treebeard/README.rst b/wagtail/vendor/django-treebeard/README.rst deleted file mode 100644 index 2c641703a..000000000 --- a/wagtail/vendor/django-treebeard/README.rst +++ /dev/null @@ -1,29 +0,0 @@ - -django-treebeard -================ - -django-treebeard is a library that implements efficient tree implementations -for the Django Web Framework 1.4+, written by Gustavo Picón and licensed under -the Apache License 2.0. - -django-treebeard is: - -- **Flexible**: Includes 3 different tree implementations with the same API: - - 1. Adjacency List - 2. Materialized Path - 3. Nested Sets - -- **Fast**: Optimized non-naive tree operations -- **Easy**: Uses Django Model Inheritance with abstract classes to define your own - models. -- **Clean**: Testable and well tested code base. Code/branch test coverage is above - 96%. Tests are available in Jenkins: - - - Test suite running on different versions of Python, Django and database - engine: https://tabo.pe/jenkins/job/django-treebeard/ - - Code quality: https://tabo.pe/jenkins/job/django-treebeard-quality/ - -You can find the documentation in - - https://tabo.pe/projects/django-treebeard/docs/tip/ diff --git a/wagtail/vendor/django-treebeard/UPDATING b/wagtail/vendor/django-treebeard/UPDATING deleted file mode 100644 index d8628bc45..000000000 --- a/wagtail/vendor/django-treebeard/UPDATING +++ /dev/null @@ -1,16 +0,0 @@ -This file documents problems you may encounter when upgrading django-treebeard -(potential backward incompatible changes). - -20081117: - - Cleaned __init__.py, if you need Node you'll have to call it from it's - original location (treebeard.models.Node instead of treebeard.Node). Also - exceptions have been moved to treebeard.exceptions. - - - -20100316: - - Queryset ordering in NS/MP trees is now enforced by the library. Previous - ordering settings in META no longer work. - diff --git a/wagtail/vendor/django-treebeard/docs/Makefile b/wagtail/vendor/django-treebeard/docs/Makefile deleted file mode 100644 index f325fb3f4..000000000 --- a/wagtail/vendor/django-treebeard/docs/Makefile +++ /dev/null @@ -1,96 +0,0 @@ -# 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) . - -.PHONY: help clean html dirhtml pickle json htmlhelp qthelp latex 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 " 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 " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" - @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)" - @echo " coverage Coverage" - -clean: - -rm -rf $(BUILDDIR)/* - -html: - mkdir -p _static - $(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." - -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-treebeard.qhcp" - @echo "To view the help file:" - @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/django-treebeard.qhc" - -latex: - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex - @echo - @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." - @echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \ - "run these through (pdf)latex." - -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." - -coverage: - $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage - @echo "Coverage, " \ - "results in $(BUILDDIR)/coverage" diff --git a/wagtail/vendor/django-treebeard/docs/README b/wagtail/vendor/django-treebeard/docs/README deleted file mode 100644 index 5f9004cde..000000000 --- a/wagtail/vendor/django-treebeard/docs/README +++ /dev/null @@ -1,7 +0,0 @@ -This is the documentation source for django-treebeard. -You can read the documentation in: - -http://docs.tabo.pe/django-treebeard/tip/ - -Or read the documentation for this version re reading the .rst files in this -directory. diff --git a/wagtail/vendor/django-treebeard/docs/_ext/djangodocs.py b/wagtail/vendor/django-treebeard/docs/_ext/djangodocs.py deleted file mode 100644 index 3b59f3c4f..000000000 --- a/wagtail/vendor/django-treebeard/docs/_ext/djangodocs.py +++ /dev/null @@ -1,10 +0,0 @@ -# taken from: -# http://reinout.vanrees.org/weblog/2012/12/01/django-intersphinx.html - - -def setup(app): - app.add_crossref_type( - directivename="setting", - rolename="setting", - indextemplate="pair: %s; setting", - ) diff --git a/wagtail/vendor/django-treebeard/docs/_static/treebeard-admin-advanced.png b/wagtail/vendor/django-treebeard/docs/_static/treebeard-admin-advanced.png deleted file mode 100644 index 28e95397f..000000000 Binary files a/wagtail/vendor/django-treebeard/docs/_static/treebeard-admin-advanced.png and /dev/null differ diff --git a/wagtail/vendor/django-treebeard/docs/_static/treebeard-admin-basic.png b/wagtail/vendor/django-treebeard/docs/_static/treebeard-admin-basic.png deleted file mode 100644 index b0d010fd8..000000000 Binary files a/wagtail/vendor/django-treebeard/docs/_static/treebeard-admin-basic.png and /dev/null differ diff --git a/wagtail/vendor/django-treebeard/docs/admin.rst b/wagtail/vendor/django-treebeard/docs/admin.rst deleted file mode 100644 index 739831048..000000000 --- a/wagtail/vendor/django-treebeard/docs/admin.rst +++ /dev/null @@ -1,52 +0,0 @@ -Admin -===== - -API ---- - -.. module:: treebeard.admin - -.. autoclass:: TreeAdmin - :show-inheritance: - - Example: - - .. code-block:: python - - from django.contrib import admin - from treebeard.admin import TreeAdmin - from treebeard.forms import movenodeform_factory - from myproject.models import MyNode - - class MyAdmin(TreeAdmin): - form = movenodeform_factory(MyNode) - - admin.site.register(MyNode, MyAdmin) - - -.. autofunction:: admin_factory - - -Interface ---------- - -The features of the admin interface will depend on the tree type. - -Advanced Interface -~~~~~~~~~~~~~~~~~~ - -:doc:`Materialized Path ` and :doc:`Nested Sets ` trees have -an AJAX interface based on `FeinCMS`_, that includes features like -drag&drop and an attractive interface. - -.. image:: _static/treebeard-admin-advanced.png - -Basic Interface -~~~~~~~~~~~~~~~ - -:doc:`Adjacency List ` trees have a basic admin interface. - -.. image:: _static/treebeard-admin-basic.png - - -.. _FeinCMS: http://www.feincms.org diff --git a/wagtail/vendor/django-treebeard/docs/al_tree.rst b/wagtail/vendor/django-treebeard/docs/al_tree.rst deleted file mode 100644 index 054aa86c5..000000000 --- a/wagtail/vendor/django-treebeard/docs/al_tree.rst +++ /dev/null @@ -1,106 +0,0 @@ -Adjacency List trees -==================== - -.. module:: treebeard.al_tree - -This is a simple implementation of the traditional Adjacency List Model for -storing trees in relational databases. - -In the adjacency list model, every node will have a -":attr:`~AL_Node.parent`" key, that will be NULL for root nodes. - -Since ``django-treebeard`` must return trees ordered in a predictable way, -the ordering for models without the :attr:`~AL_Node.node_order_by` -attribute will have an extra attribute that will store the relative -position of a node between it's siblings: :attr:`~AL_Node.sib_order`. - -The adjacency list model has the advantage of fast writes at the cost of -slow reads. If you read more than you write, use -:class:`~treebeard.mp_tree.MP_Node` instead. - -.. warning:: - - As with all tree implementations, please be aware of the - :doc:`caveats`. - - -.. inheritance-diagram:: AL_Node -.. autoclass:: AL_Node - :show-inheritance: - - .. warning:: - - If you need to define your own - :py:class:`~django.db.models.Manager` class, - you'll need to subclass - :py:class:`~AL_NodeManager`. - - - .. attribute:: node_order_by - - Attribute: a list of model fields that will be used for node - ordering. When enabled, all tree operations will assume this ordering. - - Example: - - .. code-block:: python - - node_order_by = ['field1', 'field2', 'field3'] - - .. attribute:: parent - - ``ForeignKey`` to itself. This attribute **MUST** be defined in the - subclass (sadly, this isn't inherited correctly from the ABC in - `Django 1.0`). Just copy&paste these lines to your model: - - .. code-block:: python - - parent = models.ForeignKey('self', - related_name='children_set', - null=True, - db_index=True) - - .. attribute:: sib_order - - ``PositiveIntegerField`` used to store the relative position of a node - between it's siblings. This attribute is mandatory *ONLY* if you don't - set a :attr:`node_order_by` field. You can define it copy&pasting this - line in your model: - - .. code-block:: python - - sib_order = models.PositiveIntegerField() - - Examples: - - .. code-block:: python - - class AL_TestNode(AL_Node): - parent = models.ForeignKey('self', - related_name='children_set', - null=True, - db_index=True) - sib_order = models.PositiveIntegerField() - desc = models.CharField(max_length=255) - - class AL_TestNodeSorted(AL_Node): - parent = models.ForeignKey('self', - related_name='children_set', - null=True, - db_index=True) - node_order_by = ['val1', 'val2', 'desc'] - val1 = models.IntegerField() - val2 = models.IntegerField() - desc = models.CharField(max_length=255) - - - Read the API reference of :class:`treebeard.Node` for info on methods - available in this class, or read the following section for methods with - particular arguments or exceptions. - - .. automethod:: get_depth - - See: :meth:`treebeard.Node.get_depth` - -.. autoclass:: AL_NodeManager - :show-inheritance: diff --git a/wagtail/vendor/django-treebeard/docs/api.rst b/wagtail/vendor/django-treebeard/docs/api.rst deleted file mode 100644 index 173ba37d3..000000000 --- a/wagtail/vendor/django-treebeard/docs/api.rst +++ /dev/null @@ -1,405 +0,0 @@ -API -=== - -.. module:: treebeard.models - -.. inheritance-diagram:: Node -.. autoclass:: Node - :show-inheritance: - - This is the base class that defines the API of all tree models in this - library: - - - :class:`treebeard.mp_tree.MP_Node` (materialized path) - - :class:`treebeard.ns_tree.NS_Node` (nested sets) - - :class:`treebeard.al_tree.AL_Node` (adjacency list) - - .. warning:: - - Please be aware of the :doc:`caveats` when using this library. - - .. automethod:: Node.add_root - - Example: - - .. code-block:: python - - MyNode.add_root(numval=1, strval='abcd') - - .. automethod:: add_child - - Example: - - .. code-block:: python - - node.add_child(numval=1, strval='abcd') - - .. automethod:: add_sibling - - Examples: - - .. code-block:: python - - node.add_sibling('sorted-sibling', numval=1, strval='abc') - - .. automethod:: delete - - .. note:: - - Call our queryset's delete to handle children removal. Subclasses - will handle extra maintenance. - - .. automethod:: get_tree - - .. automethod:: get_depth - - Example: - - .. code-block:: python - - node.get_depth() - - .. automethod:: get_ancestors - - Example: - - .. code-block:: python - - node.get_ancestors() - - .. automethod:: get_children - - Example: - - .. code-block:: python - - node.get_children() - - .. automethod:: get_children_count - - Example: - - .. code-block:: python - - node.get_children_count() - - .. automethod:: get_descendants - - Example: - - .. code-block:: python - - node.get_descendants() - - .. automethod:: get_descendant_count - - Example: - - .. code-block:: python - - node.get_descendant_count() - - .. automethod:: get_first_child - - Example: - - .. code-block:: python - - node.get_first_child() - - .. automethod:: get_last_child - - Example: - - .. code-block:: python - - node.get_last_child() - - .. automethod:: get_first_sibling - - Example: - - .. code-block:: python - - node.get_first_sibling() - - .. automethod:: get_last_sibling - - Example: - - .. code-block:: python - - node.get_last_sibling() - - .. automethod:: get_prev_sibling - - Example: - - .. code-block:: python - - node.get_prev_sibling() - - .. automethod:: get_next_sibling - - Example: - - .. code-block:: python - - node.get_next_sibling() - - .. automethod:: get_parent - - Example: - - .. code-block:: python - - node.get_parent() - - .. automethod:: get_root - - Example: - - .. code-block:: python - - node.get_root() - - .. automethod:: get_siblings - - Example: - - .. code-block:: python - - node.get_siblings() - - .. automethod:: is_child_of - - Example: - - .. code-block:: python - - node.is_child_of(node2) - - .. automethod:: is_descendant_of - - Example: - - .. code-block:: python - - node.is_descendant_of(node2) - - .. automethod:: is_sibling_of - - Example: - - .. code-block:: python - - node.is_sibling_of(node2) - - .. automethod:: is_root - - Example: - - .. code-block:: python - - node.is_root() - - .. automethod:: is_leaf - - Example: - - .. code-block:: python - - node.is_leaf() - - .. automethod:: move - - .. note:: The node can be moved under another root node. - - Examples: - - .. code-block:: python - - node.move(node2, 'sorted-child') - node.move(node2, 'prev-sibling') - - .. automethod:: save - - .. automethod:: get_first_root_node - - Example: - - .. code-block:: python - - MyNodeModel.get_first_root_node() - - .. automethod:: get_last_root_node - - Example: - - .. code-block:: python - - MyNodeModel.get_last_root_node() - - .. automethod:: get_root_nodes - - Example: - - .. code-block:: python - - MyNodeModel.get_root_nodes() - - .. automethod:: load_bulk - - .. note:: - - Any internal data that you may have stored in your - nodes' data (:attr:`path`, :attr:`depth`) will be - ignored. - - .. note:: - - If your node model has a ForeignKey this method will try to load - the related object before loading the data. If the related object - doesn't exist it won't load anything and will raise a DoesNotExist - exception. This is done because the dump_data method uses integers - to dump related objects. - - .. note:: - - If your node model has :attr:`node_order_by` enabled, it will - take precedence over the order in the structure. - - Example: - - .. code-block:: python - - data = [{'data':{'desc':'1'}}, - {'data':{'desc':'2'}, 'children':[ - {'data':{'desc':'21'}}, - {'data':{'desc':'22'}}, - {'data':{'desc':'23'}, 'children':[ - {'data':{'desc':'231'}}, - ]}, - {'data':{'desc':'24'}}, - ]}, - {'data':{'desc':'3'}}, - {'data':{'desc':'4'}, 'children':[ - {'data':{'desc':'41'}}, - ]}, - ] - # parent = None - MyNodeModel.load_data(data, None) - - Will create: - - .. digraph:: load_bulk_digraph - - "1"; - "2"; - "2" -> "21"; - "2" -> "22"; - "2" -> "23" -> "231"; - "2" -> "24"; - "3"; - "4"; - "4" -> "41"; - - .. automethod:: dump_bulk - - Example: - - .. code-block:: python - - tree = MyNodeModel.dump_bulk() - branch = MyNodeModel.dump_bulk(node_obj) - - .. automethod:: find_problems - - .. automethod:: fix_tree - - .. automethod:: get_descendants_group_count - - Example: - - .. code-block:: python - - # get a list of the root nodes - root_nodes = MyModel.get_descendants_group_count() - - for node in root_nodes: - print '%s by %s (%d replies)' % (node.comment, node.author, - node.descendants_count) - - .. automethod:: get_annotated_list - - - Example: - - .. code-block:: python - - annotated_list = MyModel.get_annotated_list() - - With data: - - .. digraph:: get_annotated_list_digraph - - "a"; - "a" -> "ab"; - "ab" -> "aba"; - "ab" -> "abb"; - "ab" -> "abc"; - "a" -> "ac"; - - Will return: - - .. code-block:: python - - [ - (a, {'open':True, 'close':[], 'level': 0}) - (ab, {'open':True, 'close':[], 'level': 1}) - (aba, {'open':True, 'close':[], 'level': 2}) - (abb, {'open':False, 'close':[], 'level': 2}) - (abc, {'open':False, 'close':[0,1], 'level': 2}) - (ac, {'open':False, 'close':[0], 'level': 1}) - ] - - This can be used with a template like: - - .. code-block:: django - - {% for item, info in annotated_list %} - {% if info.open %} -
  • - {% else %} -
  • - {% endif %} - - {{ item }} - - {% for close in info.close %} -
- {% endfor %} - {% endfor %} - - .. note:: - - This method was contributed originally by - `Alexey Kinyov `_, using an idea borrowed from - `django-mptt`_. - - .. versionadded:: 1.55 - - - .. automethod:: get_database_vendor - - Example: - - .. code-block:: python - - MyNodeModel.get_database_vendor("write") - - - .. versionadded:: 1.61 - - -.. _django-mptt: https://github.com/django-mptt/django-mptt/ \ No newline at end of file diff --git a/wagtail/vendor/django-treebeard/docs/caveats.rst b/wagtail/vendor/django-treebeard/docs/caveats.rst deleted file mode 100644 index 73914c94b..000000000 --- a/wagtail/vendor/django-treebeard/docs/caveats.rst +++ /dev/null @@ -1,40 +0,0 @@ -Known Caveats -============= - -Raw Queries ------------ - -``django-treebeard`` uses Django raw SQL queries for -some write operations, and raw queries don't update the objects in the -ORM since it's being bypassed. - -Because of this, if you have a node in memory and plan to use it after a -tree modification (adding/removing/moving nodes), you need to reload it. - - -Overriding the default manager ------------------------------- - -One of the most common source of bug reports in ``django-treebeard`` -is the overriding of the default managers in the subclasses. - -``django-treebeard`` relies on the default manager for correctness -and internal maintenance. If you override the default manager, -by overriding the ``objects`` member in your subclass, you -*WILL* have errors and inconsistencies in your tree. - -To avoid this problem, if you need to override the default -manager, you'll *NEED* to subclass the manager from -the base manager class for the tree you are using. - -Read the documentation in each tree type for details. - - -Custom Managers ---------------- - -Related to the previous caveat, if you need to create custom -managers, you *NEED* to subclass the manager from the -base manager class for the tree you are using. - -Read the documentation in each tree type for details. diff --git a/wagtail/vendor/django-treebeard/docs/changes.rst b/wagtail/vendor/django-treebeard/docs/changes.rst deleted file mode 100644 index 3b6a0a9cc..000000000 --- a/wagtail/vendor/django-treebeard/docs/changes.rst +++ /dev/null @@ -1,4 +0,0 @@ -Changelog -========= - -.. include:: ../CHANGES diff --git a/wagtail/vendor/django-treebeard/docs/conf.py b/wagtail/vendor/django-treebeard/docs/conf.py deleted file mode 100644 index 08584ae6b..000000000 --- a/wagtail/vendor/django-treebeard/docs/conf.py +++ /dev/null @@ -1,59 +0,0 @@ -# -*- coding: utf-8 -*- -""" - -Configuration for the Sphinx documentation generator. - -Reference: http://sphinx.pocoo.org/config.html - -""" - -import os -import sys - - -def docs_dir(): - rd = os.path.dirname(__file__) - if rd: - return rd - return '.' - - -for directory in ('_ext', '..'): - sys.path.insert(0, os.path.abspath(os.path.join(docs_dir(), directory))) - -os.environ['DJANGO_SETTINGS_MODULE'] = 'treebeard.tests.settings' - -extensions = [ - 'djangodocs', - 'sphinx.ext.autodoc', - 'sphinx.ext.coverage', - 'sphinx.ext.graphviz', - 'sphinx.ext.inheritance_diagram', - 'sphinx.ext.todo', - 'sphinx.ext.intersphinx', -] -templates_path = ['_templates'] -source_suffix = '.rst' -master_doc = 'index' -project = 'django-treebeard' -copyright = '2008-2013, Gustavo Picon' -version = '2.0b2' -release = '2.0b2' -exclude_trees = ['_build'] -pygments_style = 'sphinx' -html_theme = 'default' -html_static_path = ['_static'] -htmlhelp_basename = 'django-treebearddoc' -latex_documents = [( - 'index', - 'django-treebeard.tex', - 'django-treebeard Documentation', - 'Gustavo Picon', - 'manual')] -intersphinx_mapping = { - 'python': ('http://docs.python.org/3', None), - 'django': ( - 'https://docs.djangoproject.com/en/1.6/', - 'https://docs.djangoproject.com/en/1.6/_objects/' - ), -} diff --git a/wagtail/vendor/django-treebeard/docs/exceptions.rst b/wagtail/vendor/django-treebeard/docs/exceptions.rst deleted file mode 100644 index ba5de3797..000000000 --- a/wagtail/vendor/django-treebeard/docs/exceptions.rst +++ /dev/null @@ -1,12 +0,0 @@ -Exceptions -========== - -.. module:: treebeard.exceptions - -.. autoexception:: InvalidPosition - -.. autoexception:: InvalidMoveToDescendant - -.. autoexception:: PathOverflow - -.. autoexception:: MissingNodeOrderBy diff --git a/wagtail/vendor/django-treebeard/docs/forms.rst b/wagtail/vendor/django-treebeard/docs/forms.rst deleted file mode 100644 index 0c6d9b3b9..000000000 --- a/wagtail/vendor/django-treebeard/docs/forms.rst +++ /dev/null @@ -1,29 +0,0 @@ -Forms -===== - -.. module:: treebeard.forms - -.. autoclass:: MoveNodeForm - :show-inheritance: - -.. autofunction:: movenodeform_factory - - For a full reference of this function, please read - :py:func:`~django.forms.models.modelform_factory` - - - Example, ``MyNode`` is a subclass of :py:class:`treebeard.al_tree.AL_Node`: - - .. code-block:: python - - MyNodeForm = movenodeform_factory(MyNode) - - is equivalent to: - - .. code-block:: python - - class MyNodeForm(MoveNodeForm): - class Meta: - model = models.MyNode - exclude = ('sib_order', 'parent') - diff --git a/wagtail/vendor/django-treebeard/docs/index.rst b/wagtail/vendor/django-treebeard/docs/index.rst deleted file mode 100644 index 042c1b41d..000000000 --- a/wagtail/vendor/django-treebeard/docs/index.rst +++ /dev/null @@ -1,80 +0,0 @@ -django-treebeard -================ - -`django-treebeard `_ -is a library that implements efficient tree implementations for the -`Django Web Framework 1.4+ `_, written by -`Gustavo Picón `_ and licensed under the Apache License 2.0. - -``django-treebeard`` is: - -- **Flexible**: Includes 3 different tree implementations with the same API: - - 1. :doc:`Adjacency List ` - 2. :doc:`Materialized Path ` - 3. :doc:`Nested Sets ` - -- **Fast**: Optimized non-naive tree operations -- **Easy**: Uses Django's - :ref:`model-inheritance` with :ref:`abstract-base-classes`. - to define your own models. -- **Clean**: Testable and well tested code base. Code/branch test coverage - is above 96%. Tests are available in Jenkins: - - - `Tests running on different versions of Python, Django and DB engines`_ - - `Code Quality`_ - - -Overview --------- - -.. toctree:: - - install - tutorial - caveats - -.. toctree:: - :titlesonly: - - changes - -Reference ---------- - -.. toctree:: - - api - mp_tree - ns_tree - al_tree - exceptions - -Additional features -------------------- - -.. toctree:: - - admin - forms - -Development ------------ - -.. toctree:: - - tests - - - -.. _`Tests running on different versions of Python, Django and DB engines`: - https://tabo.pe/jenkins/job/django-treebeard/ -.. _`Code Quality`: https://tabo.pe/jenkins/job/django-treebeard-quality/ - - -Indices and tables -================== - -* :ref:`genindex` -* :ref:`modindex` -* :ref:`search` diff --git a/wagtail/vendor/django-treebeard/docs/install.rst b/wagtail/vendor/django-treebeard/docs/install.rst deleted file mode 100644 index 5025964ae..000000000 --- a/wagtail/vendor/django-treebeard/docs/install.rst +++ /dev/null @@ -1,89 +0,0 @@ -Installation -============ - - -Prerequisites -------------- - -``django-treebeard`` needs at least **Python 2.6** to run, and -**Django 1.4 or better**. - - -Installing ----------- - -You have several ways to install ``django-treebeard``. If you're not sure, -`just use pip `_ - -pip (or easy_install) -~~~~~~~~~~~~~~~~~~~~~ - -You can install the release versions from -`django-treebeard's PyPI page`_ using ``pip``: - -.. code-block:: console - - $ pip install django-treebeard - -or if for some reason you can't use ``pip``, you can try ``easy_install``, -(at your own risk): - -.. code-block:: console - - $ easy_install --always-unzip django-treebeard - - -setup.py -~~~~~~~~ - -Download a release from the `treebeard download page`_ and unpack it, then -run: - -.. code-block:: console - - $ python setup.py install - - -.deb packages -~~~~~~~~~~~~~ - -Both Debian and Ubuntu include ``django-treebeard`` as a package, so you can -just use: - -.. code-block:: console - - $ apt-get install python-django-treebeard - -or: - -.. code-block:: console - - $ aptitude install python-django-treebeard - -Remember that the packages included in linux distributions are usually not the -most recent versions. - - -Configuration -------------- - -Add ``'treebeard'`` to the -:django:setting:`INSTALLED_APPS` section in your django -settings file. - -.. note:: - - If you are going to use the :class:`~treebeard.admin.TreeAdmin` - class, you need to add the path to treebeard's templates in - :django:setting:`TEMPLATE_DIRS`. - Also you need to enable - ``django.core.context_processors.request`` - in the :django:setting:`TEMPLATE_CONTEXT_PROCESSORS` - setting in your django settings file. - - -.. _`django-treebeard's PyPI page`: - http://pypi.python.org/pypi/django-treebeard -.. _`treebeard download page`: - https://tabo.pe/projects/django-treebeard/download/ - diff --git a/wagtail/vendor/django-treebeard/docs/mp_tree.rst b/wagtail/vendor/django-treebeard/docs/mp_tree.rst deleted file mode 100644 index e3a121c38..000000000 --- a/wagtail/vendor/django-treebeard/docs/mp_tree.rst +++ /dev/null @@ -1,262 +0,0 @@ -Materialized Path trees -======================= - -.. module:: treebeard.mp_tree - -This is an efficient implementation of Materialized Path -trees for Django 1.4+, as described by `Vadim Tropashko`_ in `SQL Design -Patterns`_. Materialized Path is probably the fastest way of working with -trees in SQL without the need of extra work in the database, like Oracle's -``CONNECT BY`` or sprocs and triggers for nested intervals. - -In a materialized path approach, every node in the tree will have a -:attr:`~MP_Node.path` attribute, where the full path from the root -to the node will be stored. This has the advantage of needing very simple -and fast queries, at the risk of inconsistency because of the -denormalization of ``parent``/``child`` foreign keys. This can be prevented -with transactions. - -``django-treebeard`` uses a particular approach: every step in the path has -a fixed width and has no separators. This makes queries predictable and -faster at the cost of using more characters to store a step. To address -this problem, every step number is encoded. - -Also, two extra fields are stored in every node: -:attr:`~MP_Node.depth` and :attr:`~MP_Node.numchild`. -This makes the read operations faster, at the cost of a little more -maintenance on tree updates/inserts/deletes. Don't worry, even with these -extra steps, materialized path is more efficient than other approaches. - -.. warning:: - - As with all tree implementations, please be aware of the - :doc:`caveats`. - -.. note:: - - The materialized path approach makes heavy use of ``LIKE`` in your - database, with clauses like ``WHERE path LIKE '002003%'``. If you think - that ``LIKE`` is too slow, you're right, but in this case the - :attr:`~MP_Node.path` field is indexed in the database, and all - ``LIKE`` clauses that don't **start** with a ``%`` character will use - the index. This is what makes the materialized path approach so fast. - -.. inheritance-diagram:: MP_Node -.. autoclass:: MP_Node - :show-inheritance: - - .. warning:: - - Do not change the values of :attr:`path`, :attr:`depth` or - :attr:`numchild` directly: use one of the included methods instead. - Consider these values *read-only*. - - .. warning:: - - Do not change the values of the :attr:`steplen`, :attr:`alphabet` or - :attr:`node_order_by` after saving your first object. Doing so will - corrupt the tree. - - .. warning:: - - If you need to define your own - :py:class:`~django.db.models.Manager` class, - you'll need to subclass - :py:class:`~MP_NodeManager`. - - Also, if in your manager you need to change the default - queryset handler, you'll need to subclass - :py:class:`~MP_NodeQuerySet`. - - - Example: - - .. code-block:: python - - class SortedNode(MP_Node): - node_order_by = ['numval', 'strval'] - - numval = models.IntegerField() - strval = models.CharField(max_length=255) - - Read the API reference of :class:`treebeard.Node` for info on methods - available in this class, or read the following section for methods with - particular arguments or exceptions. - - .. attribute:: steplen - - Attribute that defines the length of each step in the :attr:`path` of - a node. The default value of *4* allows a maximum of - *1679615* children per node. Increase this value if you plan to store - large trees (a ``steplen`` of *5* allows more than *60M* children per - node). Note that increasing this value, while increasing the number of - children per node, will decrease the max :attr:`depth` of the tree (by - default: *63*). To increase the max :attr:`depth`, increase the - max_length attribute of the :attr:`path` field in your model. - - .. attribute:: alphabet - - Attribute: the alphabet that will be used in base conversions - when encoding the path steps into strings. The default value, - ``0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ`` is the most optimal possible - value that is portable between the supported databases (which means: - their default collation will order the :attr:`path` field correctly). - - .. note:: - - In case you know what you are doing, there is a test that is - disabled by default that can tell you the optimal default alphabet - in your enviroment. To run the test you must enable the - :envvar:`TREEBEARD_TEST_ALPHABET` enviroment variable: - - .. code-block:: console - - $ TREEBEARD_TEST_ALPHABET=1 python manage.py test treebeard.TestTreeAlphabet - - On my Mountain Lion system, these are the optimal values for the - three supported databases in their *default* configuration: - - ================ ================ - Database Optimal Alphabet - ================ ================ - MySQL 5.6.10 0-9A-Z - PostgreSQL 9.2.4 0-9A-Z - Sqlite3 0-9A-Z - ================ ================ - - .. attribute:: node_order_by - - Attribute: a list of model fields that will be used for node - ordering. When enabled, all tree operations will assume this ordering. - - Example: - - .. code-block:: python - - node_order_by = ['field1', 'field2', 'field3'] - - .. attribute:: path - - ``CharField``, stores the full materialized path for each node. The - default value of it's max_length, *255*, is the max efficient and - portable value for a ``varchar``. Increase it to allow deeper trees (max - depth by default: *63*) - - .. note:: - - `django-treebeard` uses Django's abstract model inheritance, so: - - 1. To change the max_length value of the path in your model, you - can't just define it since you'd get a django exception, you have - to modify the already defined attribute: - - .. code-block:: python - - class MyNodeModel(MP_Node): - pass - - MyNodeModel._meta.get_field('path').max_length = 1024 - - 2. You can't rely on Django's `auto_now` properties in date fields - for sorting, you'll have to manually set the value before creating - a node: - - .. code-block:: python - - class TestNodeSortedAutoNow(MP_Node): - desc = models.CharField(max_length=255) - created = models.DateTimeField(auto_now_add=True) - node_order_by = ['created'] - - TestNodeSortedAutoNow.add_root(desc='foo', - created=datetime.datetime.now()) - - .. note:: - - For performance, and if your database allows it, you can safely - define the path column as ASCII (not utf-8/unicode/iso8859-1/etc) to - keep the index smaller (and faster). Also note that some databases - (mysql) have a small index size limit. InnoDB for instance has a - limit of 765 bytes per index, so that would be the limit if your path - is ASCII encoded. If your path column in InnoDB is using unicode, - the index limit will be 255 characters since in MySQL's indexes, - unicode means 3 bytes per character. - - .. note:: - - ``django-treebeard`` uses `numconv`_ for path encoding. - - - .. attribute:: depth - - ``PositiveIntegerField``, depth of a node in the tree. A root node - has a depth of *1*. - - .. attribute:: numchild - - ``PositiveIntegerField``, the number of children of the node. - - .. automethod:: add_root - - See: :meth:`treebeard.Node.add_root` - - .. automethod:: add_child - - See: :meth:`treebeard.Node.add_child` - - .. automethod:: add_sibling - - See: :meth:`treebeard.Node.add_sibling` - - .. automethod:: move - - See: :meth:`treebeard.Node.move` - - .. automethod:: get_tree - - See: :meth:`treebeard.Node.get_tree` - - .. note:: - - This metod returns a queryset. - - .. automethod:: find_problems - - .. note:: - - A node won't appear in more than one list, even when it exhibits - more than one problem. This method stops checking a node when it - finds a problem and continues to the next node. - - .. note:: - - Problems 1, 2 and 3 can't be solved automatically. - - Example: - - .. code-block:: python - - MyNodeModel.find_problems() - - .. automethod:: fix_tree - - Example: - - .. code-block:: python - - MyNodeModel.fix_tree() - - - -.. autoclass:: MP_NodeManager - :show-inheritance: - -.. autoclass:: MP_NodeQuerySet - :show-inheritance: - - - -.. _`Vadim Tropashko`: http://vadimtropashko.wordpress.com/ -.. _`Sql Design Patterns`: - http://www.rampant-books.com/book_2006_1_sql_coding_styles.htm -.. _numconv: https://tabo.pe/projects/numconv/ diff --git a/wagtail/vendor/django-treebeard/docs/ns_tree.rst b/wagtail/vendor/django-treebeard/docs/ns_tree.rst deleted file mode 100644 index dcbd88828..000000000 --- a/wagtail/vendor/django-treebeard/docs/ns_tree.rst +++ /dev/null @@ -1,81 +0,0 @@ -Nested Sets trees -================= - -.. module:: treebeard.ns_tree - -An implementation of Nested Sets trees for Django 1.4+, as described by -`Joe Celko`_ in `Trees and Hierarchies in SQL for Smarties`_. - -Nested sets have very efficient reads at the cost of high maintenance on -write/delete operations. - -.. warning:: - - As with all tree implementations, please be aware of the - :doc:`caveats`. - - -.. inheritance-diagram:: NS_Node -.. autoclass:: NS_Node - :show-inheritance: - - .. warning:: - - If you need to define your own - :py:class:`~django.db.models.Manager` class, - you'll need to subclass - :py:class:`~NS_NodeManager`. - - Also, if in your manager you need to change the default - queryset handler, you'll need to subclass - :py:class:`~NS_NodeQuerySet`. - - - .. attribute:: node_order_by - - Attribute: a list of model fields that will be used for node - ordering. When enabled, all tree operations will assume this ordering. - - Example: - - .. code-block:: python - - node_order_by = ['field1', 'field2', 'field3'] - - .. attribute:: depth - - ``PositiveIntegerField``, depth of a node in the tree. A root node - has a depth of *1*. - - .. attribute:: lft - - ``PositiveIntegerField`` - - .. attribute:: rgt - - ``PositiveIntegerField`` - - .. attribute:: tree_id - - ``PositiveIntegerField`` - - .. automethod:: get_tree - - See: :meth:`treebeard.Node.get_tree` - - .. note:: - - This metod returns a queryset. - - -.. autoclass:: NS_NodeManager - :show-inheritance: - -.. autoclass:: NS_NodeQuerySet - :show-inheritance: - - - -.. _`Joe Celko`: http://en.wikipedia.org/wiki/Joe_Celko -.. _`Trees and Hierarchies in SQL for Smarties`: - http://www.elsevier.com/wps/product/cws_home/702605 diff --git a/wagtail/vendor/django-treebeard/docs/tests.rst b/wagtail/vendor/django-treebeard/docs/tests.rst deleted file mode 100644 index b34a84dd6..000000000 --- a/wagtail/vendor/django-treebeard/docs/tests.rst +++ /dev/null @@ -1,80 +0,0 @@ -Running the Test Suite -====================== - -``django-treebeard`` includes a comprehensive test suite. It is highly -recommended that you run and update the test suite when you send patches. - -py.test -------- - -You will need `pytest`_ to run the test suite. - -To run the test suite: - -.. code-block:: console - - $ py.test - -You can use all the features and plugins of pytest this way. - -By default the test suite will run using a sqlite3 database in RAM, but you can -change this setting environment variables: - -.. option:: DATABASE_ENGINE -.. option:: DATABASE_NAME -.. option:: DATABASE_USER -.. option:: DATABASE_PASSWORD -.. option:: DATABASE_HOST -.. option:: DATABASE_PORT - - Sets the database settings to be used by the test suite. Useful if you - want to test the same database engine/version you use in production. - - -tox ---- - -``django-treebeard`` uses `tox`_ to run the test suite in all the supported -environments: - - - py26-dj14-sqlite - - py26-dj14-mysql - - py26-dj14-pgsql - - py26-dj15-sqlite - - py26-dj15-mysql - - py26-dj15-pgsql - - py26-dj16-sqlite - - py26-dj16-mysql - - py26-dj16-pgsql - - py27-dj14-sqlite - - py27-dj14-mysql - - py27-dj14-pgsql - - py27-dj15-sqlite - - py27-dj15-mysql - - py27-dj15-pgsql - - py32-dj15-sqlite - - py32-dj15-pgsql - - py33-dj15-sqlite - - py33-dj15-pgsq - - py27-dj16-sqlite - - py27-dj16-mysql - - py27-dj16-pgsql - - py32-dj16-sqlite - - py32-dj16-pgsql - - py33-dj16-sqlite - - py33-dj16-pgsql - - -This means that the test suite will run 26 times to test every -environment supported by ``django-treebeard``. This takes a long time. -If you want to test only one or a few environments, please use the `-e` -option in `tox`_, like: - -.. code-block:: console - - $ tox -e py33-dj16-pgsql - - -.. _pytest: http://pytest.org/ -.. _coverage: http://nedbatchelder.com/code/coverage/ -.. _tox: http://codespeak.net/tox/ diff --git a/wagtail/vendor/django-treebeard/docs/tutorial.rst b/wagtail/vendor/django-treebeard/docs/tutorial.rst deleted file mode 100644 index 86d5756ed..000000000 --- a/wagtail/vendor/django-treebeard/docs/tutorial.rst +++ /dev/null @@ -1,106 +0,0 @@ -Tutorial -======== - -Create a basic model for your tree. In this example we'll use a Materialized -Path tree: - -.. code-block:: python - - from django.db import models - from treebeard.mp_tree import MP_Node - - class Category(MP_Node): - name = models.CharField(max_length=30) - - node_order_by = ['name'] - - def __unicode__(self): - return 'Category: %s' % self.name - - - -Run syncdb: - -.. code-block:: console - - $ python manage.py syncdb - - -Let's create some nodes: - -.. code-block:: python - - >>> from treebeard_tutorial.models import Category - >>> get = lambda node_id: Category.objects.get(pk=node_id) - >>> root = Category.add_root(name='Computer Hardware') - >>> node = get(root.pk).add_child(name='Memory') - >>> get(node.pk).add_sibling(name='Hard Drives') - - >>> get(node.pk).add_sibling(name='SSD') - - >>> get(node.pk).add_child(name='Desktop Memory') - - >>> get(node.pk).add_child(name='Laptop Memory') - - >>> get(node.pk).add_child(name='Server Memory') - - -.. note:: - - Why retrieving every node again after the first operation? Because - ``django-treebeard`` uses raw queries for most write operations, - and raw queries don't update the django objects of the db entries they - modify. See: :doc:`caveats`. - -We just created this tree: - - -.. digraph:: introduction_digraph - - "Computer Hardware"; - "Computer Hardware" -> "Hard Drives"; - "Computer Hardware" -> "Memory"; - "Memory" -> "Desktop Memory"; - "Memory" -> "Laptop Memory"; - "Memory" -> "Server Memory"; - "Computer Hardware" -> "SSD"; - - -You can see the tree structure with code: - -.. code-block:: python - - >>> Category.dump_bulk() - [{'id': 1, 'data': {'name': u'Computer Hardware'}, - 'children': [ - {'id': 3, 'data': {'name': u'Hard Drives'}}, - {'id': 2, 'data': {'name': u'Memory'}, - 'children': [ - {'id': 5, 'data': {'name': u'Desktop Memory'}}, - {'id': 6, 'data': {'name': u'Laptop Memory'}}, - {'id': 7, 'data': {'name': u'Server Memory'}}]}, - {'id': 4, 'data': {'name': u'SSD'}}]}] - >>> Category.get_annotated_list() - [(, - {'close': [], 'level': 0, 'open': True}), - (, - {'close': [], 'level': 1, 'open': True}), - (, - {'close': [], 'level': 1, 'open': False}), - (, - {'close': [], 'level': 2, 'open': True}), - (, - {'close': [], 'level': 2, 'open': False}), - (, - {'close': [0], 'level': 2, 'open': False}), - (, - {'close': [0, 1], 'level': 1, 'open': False})] - - - -Read the :class:`treebeard.models.Node` API reference for detailed info. - -.. _`treebeard mercurial repository`: - http://code.tabo.pe/django-treebeard -.. _`latest treebeard version from PyPi`: - http://pypi.python.org/pypi/django-treebeard/ diff --git a/wagtail/vendor/django-treebeard/setup.py b/wagtail/vendor/django-treebeard/setup.py deleted file mode 100644 index 379f368cc..000000000 --- a/wagtail/vendor/django-treebeard/setup.py +++ /dev/null @@ -1,59 +0,0 @@ -#!/usr/bin/env python - -import os -from setuptools import setup -from setuptools.command.test import test - - -def root_dir(): - rd = os.path.dirname(__file__) - if rd: - return rd - return '.' - - -class pytest_test(test): - def finalize_options(self): - test.finalize_options(self) - self.test_args = [] - self.test_suite = True - - def run_tests(self): - import pytest - pytest.main([]) - - -setup_args = dict( - name='django-treebeard', - version='2.0b2', - url='https://tabo.pe/projects/django-treebeard/', - author='Gustavo Picon', - author_email='tabo@tabo.pe', - license='Apache License 2.0', - packages=['treebeard', 'treebeard.templatetags', 'treebeard.tests'], - package_dir={'treebeard': 'treebeard'}, - package_data={ - 'treebeard': ['templates/admin/*.html', 'static/treebeard/*']}, - description='Efficient tree implementations for Django 1.4+', - long_description=open(root_dir() + '/README.rst').read(), - cmdclass={'test': pytest_test}, - install_requires=['Django>=1.4'], - tests_require=['pytest'], - classifiers=[ - 'Development Status :: 5 - Production/Stable', - 'Intended Audience :: Developers', - 'License :: OSI Approved :: Apache Software License', - 'Environment :: Web Environment', - 'Framework :: Django', - 'Programming Language :: Python', - 'Programming Language :: Python :: 2.6', - 'Programming Language :: Python :: 2.7', - 'Programming Language :: Python :: 3.2', - 'Programming Language :: Python :: 3.3', - 'Operating System :: OS Independent', - 'Topic :: Software Development :: Libraries', - 'Topic :: Utilities']) - - -if __name__ == '__main__': - setup(**setup_args) diff --git a/wagtail/vendor/django-treebeard/tox.ini b/wagtail/vendor/django-treebeard/tox.ini deleted file mode 100644 index dfec018a8..000000000 --- a/wagtail/vendor/django-treebeard/tox.ini +++ /dev/null @@ -1,302 +0,0 @@ -# -# tox.ini for django-treebeard -# -# Read docs/tests for help on how to use tox to run the test suite. -# - -[tox] -envlist = - py26-dj14-sqlite, - py26-dj14-mysql, - py26-dj14-pgsql, - py26-dj15-sqlite, - py26-dj15-mysql, - py26-dj15-pgsql, - py26-dj16-sqlite, - py26-dj16-mysql, - py26-dj16-pgsql, - py27-dj14-sqlite, - py27-dj14-mysql, - py27-dj14-pgsql, - py27-dj15-sqlite, - py27-dj15-mysql, - py27-dj15-pgsql, - py32-dj15-sqlite, - py32-dj15-pgsql, - py33-dj15-sqlite, - py33-dj15-pgsql - py27-dj16-sqlite, - py27-dj16-mysql, - py27-dj16-pgsql, - py32-dj16-sqlite, - py32-dj16-pgsql, - py33-dj16-sqlite, - py33-dj16-pgsql - -[testenv] -commands = - {envpython} treebeard/tests/jenkins/toxhelper.py \ - --tb=long --fulltrace -l --junitxml junit-{envname}.xml \ - {posargs} - -[testenv:docs] -basepython=python -changedir = docs -deps = - Sphinx - Django -commands = - sphinx-build -W -b html -d {envtmpdir}/doctrees . {envtmpdir}/html - -[testenv:py26-dj14-sqlite] -basepython=python2.6 -deps = - Django>=1.4,<1.5 - coverage - pytest -setenv = - DATABASE_ENGINE=sqlite3 - -[testenv:py26-dj14-mysql] -basepython=python2.6 -deps = - Django>=1.4,<1.5 - MySQL-python - coverage - pytest -setenv = - DATABASE_ENGINE=mysql - -[testenv:py26-dj14-pgsql] -basepython=python2.6 -deps = - Django>=1.4,<1.5 - psycopg2>2.4.1 - coverage - pytest -setenv = - DATABASE_ENGINE=postgresql_psycopg2 - -[testenv:py26-dj15-sqlite] -basepython=python2.6 -deps = - Django>=1.5,<1.6 - coverage - pytest -setenv = - DATABASE_ENGINE=sqlite3 - -[testenv:py26-dj15-mysql] -basepython=python2.6 -deps = - Django>=1.5,<1.6 - MySQL-python - coverage - pytest -setenv = - DATABASE_ENGINE=mysql - -[testenv:py26-dj15-pgsql] -basepython=python2.6 -deps = - Django>=1.5,<1.6 - psycopg2>2.4.1 - coverage - pytest -setenv = - DATABASE_ENGINE=postgresql_psycopg2 - -[testenv:py26-dj16-sqlite] -basepython=python2.6 -deps = - Django>=1.6,<1.7 - coverage - pytest -setenv = - DATABASE_ENGINE=sqlite3 - -[testenv:py26-dj16-mysql] -basepython=python2.6 -deps = - Django>=1.6,<1.7 - MySQL-python - coverage - pytest -setenv = - DATABASE_ENGINE=mysql - -[testenv:py26-dj16-pgsql] -basepython=python2.6 -deps = - Django>=1.6,<1.7 - psycopg2>2.4.1 - coverage - pytest -setenv = - DATABASE_ENGINE=postgresql_psycopg2 - -[testenv:py27-dj14-sqlite] -basepython=python2.7 -deps = - Django>=1.4,<1.5 - coverage - pytest -setenv = - DATABASE_ENGINE=sqlite3 - -[testenv:py27-dj14-mysql] -basepython=python2.7 -deps = - Django>=1.4,<1.5 - MySQL-python - coverage - pytest -setenv = - DATABASE_ENGINE=mysql - -[testenv:py27-dj14-pgsql] -basepython=python2.7 -deps = - Django>=1.4,<1.5 - psycopg2>2.4.1 - coverage - pytest -setenv = - DATABASE_ENGINE=postgresql_psycopg2 - -[testenv:py27-dj15-sqlite] -basepython=python2.7 -deps = - Django>=1.5,<1.6 - coverage - pytest -setenv = - DATABASE_ENGINE=sqlite3 - -[testenv:py27-dj15-mysql] -basepython=python2.7 -deps = - Django>=1.5,<1.6 - MySQL-python - coverage - pytest -setenv = - DATABASE_ENGINE=mysql - -[testenv:py27-dj15-pgsql] -basepython=python2.7 -deps = - Django>=1.5,<1.6 - psycopg2>2.4.1 - coverage - pytest -setenv = - DATABASE_ENGINE=postgresql_psycopg2 - -[testenv:py32-dj15-sqlite] -basepython=python3.2 -deps = - Django>=1.5,<1.6 - coverage - pytest -setenv = - DATABASE_ENGINE=sqlite3 - -[testenv:py32-dj15-pgsql] -basepython=python3.2 -deps = - Django>=1.5,<1.6 - psycopg2 - coverage - pytest -setenv = - DATABASE_ENGINE=postgresql_psycopg2 - -[testenv:py33-dj15-sqlite] -basepython=python3.3 -deps = - Django>=1.5,<1.6 - coverage - pytest -setenv = - DATABASE_ENGINE=sqlite3 - -[testenv:py33-dj15-pgsql] -basepython=python3.3 -deps = - Django>=1.5,<1.6 - psycopg2 - coverage - pytest -setenv = - DATABASE_ENGINE=postgresql_psycopg2 - - -[testenv:py27-dj16-sqlite] -basepython=python2.7 -deps = - Django>=1.6,<1.7 - coverage - pytest -setenv = - DATABASE_ENGINE=sqlite3 - -[testenv:py27-dj16-mysql] -basepython=python2.7 -deps = - Django>=1.6,<1.7 - MySQL-python - coverage - pytest -setenv = - DATABASE_ENGINE=mysql - -[testenv:py27-dj16-pgsql] -basepython=python2.7 -deps = - Django>=1.6,<1.7 - psycopg2>2.4.1 - coverage - pytest -setenv = - DATABASE_ENGINE=postgresql_psycopg2 - -[testenv:py32-dj16-sqlite] -basepython=python3.2 -deps = - Django>=1.6,<1.7 - coverage - pytest -setenv = - DATABASE_ENGINE=sqlite3 - -[testenv:py32-dj16-pgsql] -basepython=python3.2 -deps = - Django>=1.6,<1.7 - psycopg2 - coverage - pytest -setenv = - DATABASE_ENGINE=postgresql_psycopg2 - -[testenv:py33-dj16-sqlite] -basepython=python3.3 -deps = - Django>=1.6,<1.7 - coverage - pytest -setenv = - DATABASE_ENGINE=sqlite3 - -[testenv:py33-dj16-pgsql] -basepython=python3.3 -deps = - Django>=1.6,<1.7 - psycopg2 - coverage - pytest -setenv = - DATABASE_ENGINE=postgresql_psycopg2 - - diff --git a/wagtail/vendor/django-treebeard/treebeard/__init__.py b/wagtail/vendor/django-treebeard/treebeard/__init__.py deleted file mode 100644 index c35bfc233..000000000 --- a/wagtail/vendor/django-treebeard/treebeard/__init__.py +++ /dev/null @@ -1 +0,0 @@ -__version__ = '2.0b2' diff --git a/wagtail/vendor/django-treebeard/treebeard/admin.py b/wagtail/vendor/django-treebeard/treebeard/admin.py deleted file mode 100644 index a461d3754..000000000 --- a/wagtail/vendor/django-treebeard/treebeard/admin.py +++ /dev/null @@ -1,111 +0,0 @@ -"""Django admin support for treebeard""" - -import sys - -from django.conf.urls import patterns, url - -from django.contrib import admin, messages -from django.http import HttpResponse, HttpResponseBadRequest -from django.utils.translation import ugettext_lazy as _ -if sys.version_info >= (3, 0): - from django.utils.encoding import force_str -else: - from django.utils.encoding import force_unicode as force_str - -from treebeard.exceptions import (InvalidPosition, MissingNodeOrderBy, - InvalidMoveToDescendant, PathOverflow) -from treebeard.al_tree import AL_Node - - -class TreeAdmin(admin.ModelAdmin): - """Django Admin class for treebeard.""" - - change_list_template = 'admin/tree_change_list.html' - - def queryset(self, request): - if issubclass(self.model, AL_Node): - # AL Trees return a list instead of a QuerySet for .get_tree() - # So we're returning the regular .queryset cause we will use - # the old admin - return super(TreeAdmin, self).queryset(request) - else: - return self.model.get_tree() - - def changelist_view(self, request, extra_context=None): - if issubclass(self.model, AL_Node): - # For AL trees, use the old admin display - self.change_list_template = 'admin/tree_list.html' - return super(TreeAdmin, self).changelist_view(request, extra_context) - - def get_urls(self): - """ - Adds a url to move nodes to this admin - """ - urls = super(TreeAdmin, self).get_urls() - new_urls = patterns( - '', - url('^move/$', self.admin_site.admin_view(self.move_node), ), - url(r'^jsi18n/$', 'django.views.i18n.javascript_catalog', - {'packages': ('treebeard',)}), - ) - return new_urls + urls - - def get_node(self, node_id): - return self.model.objects.get(pk=node_id) - - def try_to_move_node(self, as_child, node, pos, request, target): - try: - node.move(target, pos=pos) - # Call the save method on the (reloaded) node in order to trigger - # possible signal handlers etc. - node = self.get_node(node.pk) - node.save() - except (MissingNodeOrderBy, PathOverflow, InvalidMoveToDescendant, - InvalidPosition): - e = sys.exc_info()[1] - # An error was raised while trying to move the node, then set an - # error message and return 400, this will cause a reload on the - # client to show the message - messages.error(request, - _('Exception raised while moving node: %s') % _( - force_str(e))) - return HttpResponseBadRequest('Exception raised during move') - if as_child: - msg = _('Moved node "%(node)s" as child of "%(other)s"') - else: - msg = _('Moved node "%(node)s" as sibling of "%(other)s"') - messages.info(request, msg % {'node': node, 'other': target}) - return HttpResponse('OK') - - def move_node(self, request): - try: - node_id = request.POST['node_id'] - target_id = request.POST['sibling_id'] - as_child = bool(int(request.POST.get('as_child', 0))) - except (KeyError, ValueError): - # Some parameters were missing return a BadRequest - return HttpResponseBadRequest('Malformed POST params') - - node = self.get_node(node_id) - target = self.get_node(target_id) - is_sorted = True if node.node_order_by else False - - pos = { - (True, True): 'sorted-child', - (True, False): 'last-child', - (False, True): 'sorted-sibling', - (False, False): 'left', - }[as_child, is_sorted] - return self.try_to_move_node(as_child, node, pos, request, target) - - -def admin_factory(form_class): - """Dynamically build a TreeAdmin subclass for the given form class. - - :param form_class: - :return: A TreeAdmin subclass. - """ - return type( - form_class.__name__ + 'Admin', - (TreeAdmin,), - dict(form=form_class)) diff --git a/wagtail/vendor/django-treebeard/treebeard/al_tree.py b/wagtail/vendor/django-treebeard/treebeard/al_tree.py deleted file mode 100644 index 57a23200d..000000000 --- a/wagtail/vendor/django-treebeard/treebeard/al_tree.py +++ /dev/null @@ -1,334 +0,0 @@ -"""Adjacency List""" - -from django.core import serializers -from django.db import connection, models, transaction -from django.utils.translation import ugettext_noop as _ - -from treebeard.exceptions import InvalidMoveToDescendant -from treebeard.models import Node - - -class AL_NodeManager(models.Manager): - """Custom manager for nodes in an Adjacency List tree.""" - - def get_query_set(self): - """Sets the custom queryset as the default.""" - if self.model.node_order_by: - order_by = ['parent'] + list(self.model.node_order_by) - else: - order_by = ['parent', 'sib_order'] - return super(AL_NodeManager, self).get_query_set().order_by(*order_by) - - -class AL_Node(Node): - """Abstract model to create your own Adjacency List Trees.""" - - objects = AL_NodeManager() - node_order_by = None - - @classmethod - def add_root(cls, **kwargs): - """Adds a root node to the tree.""" - newobj = cls(**kwargs) - newobj._cached_depth = 1 - if not cls.node_order_by: - try: - max = cls.objects.filter(parent__isnull=True).order_by( - 'sib_order').reverse()[0].sib_order - except IndexError: - max = 0 - newobj.sib_order = max + 1 - newobj.save() - transaction.commit_unless_managed() - return newobj - - @classmethod - def get_root_nodes(cls): - """:returns: A queryset containing the root nodes in the tree.""" - return cls.objects.filter(parent__isnull=True) - - def get_depth(self, update=False): - """ - :returns: the depth (level) of the node - Caches the result in the object itself to help in loops. - - :param update: Updates the cached value. - """ - - if self.parent_id is None: - return 1 - - try: - if update: - del self._cached_depth - else: - return self._cached_depth - except AttributeError: - pass - - depth = 0 - node = self - while node: - node = node.parent - depth += 1 - self._cached_depth = depth - return depth - - def get_children(self): - """:returns: A queryset of all the node's children""" - return self.__class__.objects.filter(parent=self) - - def get_parent(self, update=False): - """:returns: the parent node of the current node object.""" - return self.parent - - def get_ancestors(self): - """ - :returns: A *list* containing the current node object's ancestors, - starting by the root node and descending to the parent. - """ - ancestors = [] - node = self.parent - while node: - ancestors.insert(0, node) - node = node.parent - return ancestors - - def get_root(self): - """:returns: the root node for the current node object.""" - ancestors = self.get_ancestors() - if ancestors: - return ancestors[0] - return self - - def is_descendant_of(self, node): - """ - :returns: ``True`` if the node if a descendant of another node given - as an argument, else, returns ``False`` - """ - return self.pk in [obj.pk for obj in node.get_descendants()] - - @classmethod - def dump_bulk(cls, parent=None, keep_ids=True): - """Dumps a tree branch to a python data structure.""" - - serializable_cls = cls._get_serializable_model() - if ( - parent and serializable_cls != cls and - parent.__class__ != serializable_cls - ): - parent = serializable_cls.objects.get(pk=parent.pk) - - # a list of nodes: not really a queryset, but it works - objs = serializable_cls.get_tree(parent) - - ret, lnk = [], {} - for node, pyobj in zip(objs, serializers.serialize('python', objs)): - depth = node.get_depth() - # django's serializer stores the attributes in 'fields' - fields = pyobj['fields'] - del fields['parent'] - - # non-sorted trees have this - if 'sib_order' in fields: - del fields['sib_order'] - - if 'id' in fields: - del fields['id'] - - newobj = {'data': fields} - if keep_ids: - newobj['id'] = pyobj['pk'] - - if (not parent and depth == 1) or\ - (parent and depth == parent.get_depth()): - ret.append(newobj) - else: - parentobj = lnk[node.parent_id] - if 'children' not in parentobj: - parentobj['children'] = [] - parentobj['children'].append(newobj) - lnk[node.pk] = newobj - return ret - - def add_child(self, **kwargs): - """Adds a child to the node.""" - newobj = self.__class__(**kwargs) - try: - newobj._cached_depth = self._cached_depth + 1 - except AttributeError: - pass - if not self.__class__.node_order_by: - try: - max = self.__class__.objects.filter(parent=self).reverse( - )[0].sib_order - except IndexError: - max = 0 - newobj.sib_order = max + 1 - newobj.parent = self - newobj.save() - transaction.commit_unless_managed() - return newobj - - @classmethod - def _get_tree_recursively(cls, results, parent, depth): - if parent: - nodes = parent.get_children() - else: - nodes = cls.get_root_nodes() - for node in nodes: - node._cached_depth = depth - results.append(node) - cls._get_tree_recursively(results, node, depth + 1) - - @classmethod - def get_tree(cls, parent=None): - """ - :returns: A list of nodes ordered as DFS, including the parent. If - no parent is given, the entire tree is returned. - """ - if parent: - depth = parent.get_depth() + 1 - results = [parent] - else: - depth = 1 - results = [] - cls._get_tree_recursively(results, parent, depth) - return results - - def get_descendants(self): - """ - :returns: A *list* of all the node's descendants, doesn't - include the node itself - """ - return self.__class__.get_tree(parent=self)[1:] - - def get_descendant_count(self): - """:returns: the number of descendants of a nodee""" - return len(self.get_descendants()) - - def get_siblings(self): - """ - :returns: A queryset of all the node's siblings, including the node - itself. - """ - if self.parent: - return self.__class__.objects.filter(parent=self.parent) - return self.__class__.get_root_nodes() - - def add_sibling(self, pos=None, **kwargs): - """Adds a new node as a sibling to the current node object.""" - pos = self._prepare_pos_var_for_add_sibling(pos) - newobj = self.__class__(**kwargs) - if not self.node_order_by: - newobj.sib_order = self.__class__._get_new_sibling_order(pos, - self) - newobj.parent_id = self.parent_id - newobj.save() - transaction.commit_unless_managed() - return newobj - - @classmethod - def _is_target_pos_the_last_sibling(cls, pos, target): - return pos == 'last-sibling' or ( - pos == 'right' and target == target.get_last_sibling()) - - @classmethod - def _make_hole_in_db(cls, min, target_node): - qset = cls.objects.filter(sib_order__gte=min) - if target_node.is_root(): - qset = qset.filter(parent__isnull=True) - else: - qset = qset.filter(parent=target_node.parent) - qset.update(sib_order=models.F('sib_order') + 1) - - @classmethod - def _make_hole_and_get_sibling_order(cls, pos, target_node): - siblings = target_node.get_siblings() - siblings = { - 'left': siblings.filter(sib_order__gte=target_node.sib_order), - 'right': siblings.filter(sib_order__gt=target_node.sib_order), - 'first-sibling': siblings - }[pos] - sib_order = { - 'left': target_node.sib_order, - 'right': target_node.sib_order + 1, - 'first-sibling': 1 - }[pos] - try: - min = siblings.order_by('sib_order')[0].sib_order - except IndexError: - min = 0 - if min: - cls._make_hole_in_db(min, target_node) - return sib_order - - @classmethod - def _get_new_sibling_order(cls, pos, target_node): - if cls._is_target_pos_the_last_sibling(pos, target_node): - sib_order = target_node.get_last_sibling().sib_order + 1 - else: - sib_order = cls._make_hole_and_get_sibling_order(pos, target_node) - return sib_order - - def move(self, target, pos=None): - """ - Moves the current node and all it's descendants to a new position - relative to another node. - """ - - pos = self._prepare_pos_var_for_move(pos) - - sib_order = None - parent = None - - if pos in ('first-child', 'last-child', 'sorted-child'): - # moving to a child - if not target.is_leaf(): - target = target.get_last_child() - pos = {'first-child': 'first-sibling', - 'last-child': 'last-sibling', - 'sorted-child': 'sorted-sibling'}[pos] - else: - parent = target - if pos == 'sorted-child': - pos = 'sorted-sibling' - else: - pos = 'first-sibling' - sib_order = 1 - - if target.is_descendant_of(self): - raise InvalidMoveToDescendant( - _("Can't move node to a descendant.")) - - if self == target and ( - (pos == 'left') or - (pos in ('right', 'last-sibling') and - target == target.get_last_sibling()) or - (pos == 'first-sibling' and - target == target.get_first_sibling())): - # special cases, not actually moving the node so no need to UPDATE - return - - if pos == 'sorted-sibling': - if parent: - self.parent = parent - else: - self.parent = target.parent - else: - if sib_order: - self.sib_order = sib_order - else: - self.sib_order = self.__class__._get_new_sibling_order(pos, - target) - if parent: - self.parent = parent - else: - self.parent = target.parent - - self.save() - transaction.commit_unless_managed() - - class Meta: - """Abstract model.""" - abstract = True diff --git a/wagtail/vendor/django-treebeard/treebeard/exceptions.py b/wagtail/vendor/django-treebeard/treebeard/exceptions.py deleted file mode 100644 index 99bdcee1b..000000000 --- a/wagtail/vendor/django-treebeard/treebeard/exceptions.py +++ /dev/null @@ -1,24 +0,0 @@ -"""Treebeard exceptions""" - - -class InvalidPosition(Exception): - """Raised when passing an invalid pos value""" - - -class InvalidMoveToDescendant(Exception): - """Raised when attemping to move a node to one of it's descendants.""" - - -class MissingNodeOrderBy(Exception): - """ - Raised when an operation needs a missing - :attr:`~treebeard.MP_Node.node_order_by` attribute - """ - - -class PathOverflow(Exception): - """ - Raised when trying to add or move a node to a position where no more nodes - can be added (see :attr:`~treebeard.MP_Node.path` and - :attr:`~treebeard.MP_Node.alphabet` for more info) - """ diff --git a/wagtail/vendor/django-treebeard/treebeard/forms.py b/wagtail/vendor/django-treebeard/treebeard/forms.py deleted file mode 100644 index e5f7ebbcf..000000000 --- a/wagtail/vendor/django-treebeard/treebeard/forms.py +++ /dev/null @@ -1,229 +0,0 @@ -"""Forms for treebeard.""" - -from django import forms -from django.db.models.query import QuerySet -from django.forms.models import BaseModelForm, ErrorList, model_to_dict -from django.forms.models import modelform_factory as django_modelform_factory -from django.utils.safestring import mark_safe -from django.utils.translation import ugettext_lazy as _ - -from treebeard.al_tree import AL_Node -from treebeard.mp_tree import MP_Node -from treebeard.ns_tree import NS_Node - - -class MoveNodeForm(forms.ModelForm): - """ - Form to handle moving a node in a tree. - - Handles sorted/unsorted trees. - - It adds two fields to the form: - - - Relative to: The target node where the current node will - be moved to. - - Position: The position relative to the target node that - will be used to move the node. These can be: - - - For sorted trees: ``Child of`` and ``Sibling of`` - - For unsorted trees: ``First child of``, ``Before`` and - ``After`` - - .. warning:: - - Subclassing :py:class:`MoveNodeForm` directly is - discouraged, since special care is needed to handle - excluded fields, and these change depending on the - tree type. - - It is recommended that the :py:func:`movenodeform_factory` - function is used instead. - - """ - - __position_choices_sorted = ( - ('sorted-child', _('Child of')), - ('sorted-sibling', _('Sibling of')), - ) - - __position_choices_unsorted = ( - ('first-child', _('First child of')), - ('left', _('Before')), - ('right', _('After')), - ) - - _position = forms.ChoiceField(required=True, label=_("Position")) - - _ref_node_id = forms.TypedChoiceField(required=False, - coerce=int, - label=_("Relative to")) - - def _get_position_ref_node(self, instance): - if self.is_sorted: - position = 'sorted-child' - node_parent = instance.get_parent() - if node_parent: - ref_node_id = node_parent.pk - else: - ref_node_id = '' - else: - prev_sibling = instance.get_prev_sibling() - if prev_sibling: - position = 'right' - ref_node_id = prev_sibling.pk - else: - position = 'first-child' - if instance.is_root(): - ref_node_id = '' - else: - ref_node_id = instance.get_parent().pk - return {'_ref_node_id': ref_node_id, - '_position': position} - - def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None, - initial=None, error_class=ErrorList, label_suffix=':', - empty_permitted=False, instance=None): - opts = self._meta - if instance is None: - if opts.model is None: - raise ValueError('MoveNodeForm has no model class specified.') - else: - opts.model = type(instance) - self.is_sorted = getattr(opts.model, 'node_order_by', False) - - if self.is_sorted: - choices_sort_mode = self.__class__.__position_choices_sorted - else: - choices_sort_mode = self.__class__.__position_choices_unsorted - self.declared_fields['_position'].choices = choices_sort_mode - - if instance is None: - # if we didn't get an instance, instantiate a new one - instance = opts.model() - object_data = {} - choices_for_node = None - else: - object_data = model_to_dict(instance, opts.fields, opts.exclude) - object_data.update(self._get_position_ref_node(instance)) - choices_for_node = instance - - choices = self.mk_dropdown_tree(opts.model, for_node=choices_for_node) - self.declared_fields['_ref_node_id'].choices = choices - self.instance = instance - # if initial was provided, it should override the values from instance - if initial is not None: - object_data.update(initial) - super(BaseModelForm, self).__init__(data, files, auto_id, prefix, - object_data, error_class, - label_suffix, empty_permitted) - - def _clean_cleaned_data(self): - """ delete auxilary fields not belonging to node model """ - reference_node_id = 0 - - if '_ref_node_id' in self.cleaned_data: - reference_node_id = self.cleaned_data['_ref_node_id'] - del self.cleaned_data['_ref_node_id'] - - position_type = self.cleaned_data['_position'] - del self.cleaned_data['_position'] - - return position_type, reference_node_id - - def save(self, commit=True): - position_type, reference_node_id = self._clean_cleaned_data() - - if self.instance.pk is None: - cl_data = {} - for field in self.cleaned_data: - if not isinstance(self.cleaned_data[field], (list, QuerySet)): - cl_data[field] = self.cleaned_data[field] - if reference_node_id: - reference_node = self._meta.model.objects.get( - pk=reference_node_id) - self.instance = reference_node.add_child(**cl_data) - self.instance.move(reference_node, pos=position_type) - else: - self.instance = self._meta.model.add_root(**cl_data) - else: - self.instance.save() - if reference_node_id: - reference_node = self._meta.model.objects.get( - pk=reference_node_id) - self.instance.move(reference_node, pos=position_type) - else: - if self.is_sorted: - pos = 'sorted-sibling' - else: - pos = 'first-sibling' - self.instance.move(self._meta.model.get_first_root_node(), pos) - # Reload the instance - self.instance = self._meta.model.objects.get(pk=self.instance.pk) - super(MoveNodeForm, self).save(commit=commit) - return self.instance - - @staticmethod - def is_loop_safe(for_node, possible_parent): - if for_node is not None: - return not ( - possible_parent == for_node - ) or (possible_parent.is_descendant_of(for_node)) - return True - - @staticmethod - def mk_indent(level): - return '    ' * (level - 1) - - @classmethod - def add_subtree(cls, for_node, node, options): - """ Recursively build options tree. """ - if cls.is_loop_safe(for_node, node): - options.append( - (node.pk, - mark_safe(cls.mk_indent(node.get_depth()) + str(node)))) - for subnode in node.get_children(): - cls.add_subtree(for_node, subnode, options) - - @classmethod - def mk_dropdown_tree(cls, model, for_node=None): - """ Creates a tree-like list of choices """ - - options = [(0, _('-- root --'))] - for node in model.get_root_nodes(): - cls.add_subtree(for_node, node, options) - return options - - -def movenodeform_factory(model, form=MoveNodeForm, fields=None, exclude=None, - formfield_callback=None, widgets=None): - """Dynamically build a MoveNodeForm subclass with the proper Meta. - - :param Node model: - - The subclass of :py:class:`Node` that will be handled - by the form. - - :param form: - - The form class that will be used as a base. By - default, :py:class:`MoveNodeForm` will be used. - - :return: A :py:class:`MoveNodeForm` subclass - """ - _exclude = _get_exclude_for_model(model, exclude) - return django_modelform_factory( - model, form, fields, _exclude, formfield_callback, widgets) - - -def _get_exclude_for_model(model, exclude): - if exclude: - _exclude = tuple(exclude) - else: - _exclude = () - if issubclass(model, AL_Node): - _exclude += ('sib_order', 'parent') - elif issubclass(model, MP_Node): - _exclude += ('depth', 'numchild', 'path') - elif issubclass(model, NS_Node): - _exclude += ('depth', 'lft', 'rgt', 'tree_id') - return _exclude diff --git a/wagtail/vendor/django-treebeard/treebeard/locale/es/LC_MESSAGES/django.mo b/wagtail/vendor/django-treebeard/treebeard/locale/es/LC_MESSAGES/django.mo deleted file mode 100644 index 9c35c4b14..000000000 Binary files a/wagtail/vendor/django-treebeard/treebeard/locale/es/LC_MESSAGES/django.mo and /dev/null differ diff --git a/wagtail/vendor/django-treebeard/treebeard/locale/es/LC_MESSAGES/django.po b/wagtail/vendor/django-treebeard/treebeard/locale/es/LC_MESSAGES/django.po deleted file mode 100644 index c950ba585..000000000 --- a/wagtail/vendor/django-treebeard/treebeard/locale/es/LC_MESSAGES/django.po +++ /dev/null @@ -1,78 +0,0 @@ -msgid "" -msgstr "" -"Project-Id-Version: Django-treebeard\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2011-07-18 17:36+0200\n" -"PO-Revision-Date: 2010-05-03 23:40-0500\n" -"Last-Translator: Gustavo Picon \n" -"Language-Team: Spanish\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" - -#: admin.py:113 -#, python-format -msgid "Moved node \"%(node)s\" as child of \"%(other)s\"" -msgstr "" - -#: admin.py:119 -#, python-format -msgid "Moved node \"%(node)s\" as sibling of \"%(other)s\"" -msgstr "" - -#: admin.py:129 -#, python-format -msgid "Exception raised while moving node: %s" -msgstr "" - -#: al_tree.py:319 mp_tree.py:641 ns_tree.py:308 -msgid "Can't move node to a descendant." -msgstr "" - -#: forms.py:17 -msgid "Child of" -msgstr "Hijo de" - -#: forms.py:18 -msgid "Sibling of" -msgstr "Hermano de" - -#: forms.py:22 -msgid "First child of" -msgstr "Primer hijo de" - -#: forms.py:23 -msgid "Before" -msgstr "Antes" - -#: forms.py:24 -msgid "After" -msgstr "Después" - -#: forms.py:27 -msgid "Position" -msgstr "Posición" - -#: forms.py:31 -msgid "Relative to" -msgstr "Relativo a" - -#: forms.py:81 -msgid "-- root --" -msgstr "-- raíz --" - -#: mp_tree.py:521 -msgid "" -"The new node is too deep in the tree, try increasing the path.max_length " -"property and UPDATE your database" -msgstr "" - -#: mp_tree.py:702 -#, python-format -msgid "Path Overflow from: '%s'" -msgstr "" - -#: templatetags/admin_tree.py:148 -msgid "Return to ordered tree" -msgstr "" diff --git a/wagtail/vendor/django-treebeard/treebeard/locale/es/LC_MESSAGES/djangojs.mo b/wagtail/vendor/django-treebeard/treebeard/locale/es/LC_MESSAGES/djangojs.mo deleted file mode 100644 index 81f016d09..000000000 Binary files a/wagtail/vendor/django-treebeard/treebeard/locale/es/LC_MESSAGES/djangojs.mo and /dev/null differ diff --git a/wagtail/vendor/django-treebeard/treebeard/locale/es/LC_MESSAGES/djangojs.po b/wagtail/vendor/django-treebeard/treebeard/locale/es/LC_MESSAGES/djangojs.po deleted file mode 100644 index bf30b8658..000000000 --- a/wagtail/vendor/django-treebeard/treebeard/locale/es/LC_MESSAGES/djangojs.po +++ /dev/null @@ -1,24 +0,0 @@ -msgid "" -msgstr "" -"Project-Id-Version: Django-treebeard\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2011-07-18 14:12+0200\n" -"PO-Revision-Date: 2011-07-18 14:12+0200\n" -"Last-Translator: \n" -"Language-Team: Spanish\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" - -#: static/treebeard/treebeard-admin.js:157 -msgid "Abort" -msgstr "" - -#: static/treebeard/treebeard-admin.js:172 -msgid "As Sibling" -msgstr "" - -#: static/treebeard/treebeard-admin.js:190 -msgid "As child" -msgstr "" diff --git a/wagtail/vendor/django-treebeard/treebeard/locale/nl/LC_MESSAGES/django.mo b/wagtail/vendor/django-treebeard/treebeard/locale/nl/LC_MESSAGES/django.mo deleted file mode 100644 index c5aad5c26..000000000 Binary files a/wagtail/vendor/django-treebeard/treebeard/locale/nl/LC_MESSAGES/django.mo and /dev/null differ diff --git a/wagtail/vendor/django-treebeard/treebeard/locale/nl/LC_MESSAGES/django.po b/wagtail/vendor/django-treebeard/treebeard/locale/nl/LC_MESSAGES/django.po deleted file mode 100644 index 6cea35f1e..000000000 --- a/wagtail/vendor/django-treebeard/treebeard/locale/nl/LC_MESSAGES/django.po +++ /dev/null @@ -1,80 +0,0 @@ -msgid "" -msgstr "" -"Project-Id-Version: Django-treebeard\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2011-07-18 17:36+0200\n" -"PO-Revision-Date: 2011-07-18 14:11+0200\n" -"Last-Translator: Jaap Roes \n" -"Language-Team: Dutch\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" - -#: admin.py:113 -#, python-format -msgid "Moved node \"%(node)s\" as child of \"%(other)s\"" -msgstr "\"%(node)s\" is nu onderdeel van \"%(other)s\"" - -#: admin.py:119 -#, python-format -msgid "Moved node \"%(node)s\" as sibling of \"%(other)s\"" -msgstr "\"%(node)s\" staat nu voor \"%(other)s\"" - -#: admin.py:129 -#, python-format -msgid "Exception raised while moving node: %s" -msgstr "Fatale fout tijdens het verplaatsen: %s" - -#: al_tree.py:319 mp_tree.py:641 ns_tree.py:308 -msgid "Can't move node to a descendant." -msgstr "Kan node niet naar eigen subnode verplaatsen" - -#: forms.py:17 -msgid "Child of" -msgstr "Onderdeel" - -#: forms.py:18 -msgid "Sibling of" -msgstr "Naast" - -#: forms.py:22 -msgid "First child of" -msgstr "1e onderdeel" - -#: forms.py:23 -msgid "Before" -msgstr "Voor" - -#: forms.py:24 -msgid "After" -msgstr "Na" - -#: forms.py:27 -msgid "Position" -msgstr "Positie" - -#: forms.py:31 -msgid "Relative to" -msgstr "Ten opzichte van" - -#: forms.py:81 -msgid "-- root --" -msgstr "-- hoofdniveau --" - -#: mp_tree.py:521 -msgid "" -"The new node is too deep in the tree, try increasing the path.max_length " -"property and UPDATE your database" -msgstr "" -"De nieuwe node bevindt zich te diep in de boom. Verhoog de path.max_lenght " -"waarde en UPDATE de database." - -#: mp_tree.py:702 -#, python-format -msgid "Path Overflow from: '%s'" -msgstr "Path overflow van: '%s'" - -#: templatetags/admin_tree.py:148 -msgid "Return to ordered tree" -msgstr "Als gesorteerde boom" diff --git a/wagtail/vendor/django-treebeard/treebeard/locale/nl/LC_MESSAGES/djangojs.mo b/wagtail/vendor/django-treebeard/treebeard/locale/nl/LC_MESSAGES/djangojs.mo deleted file mode 100644 index 7ef0783ed..000000000 Binary files a/wagtail/vendor/django-treebeard/treebeard/locale/nl/LC_MESSAGES/djangojs.mo and /dev/null differ diff --git a/wagtail/vendor/django-treebeard/treebeard/locale/nl/LC_MESSAGES/djangojs.po b/wagtail/vendor/django-treebeard/treebeard/locale/nl/LC_MESSAGES/djangojs.po deleted file mode 100644 index 773936342..000000000 --- a/wagtail/vendor/django-treebeard/treebeard/locale/nl/LC_MESSAGES/djangojs.po +++ /dev/null @@ -1,24 +0,0 @@ -msgid "" -msgstr "" -"Project-Id-Version: Django-treebeard\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2011-07-18 14:12+0200\n" -"PO-Revision-Date: 2011-07-18 14:12+0200\n" -"Last-Translator: Jaap Roes \n" -"Language-Team: Dutch\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" - -#: static/treebeard/treebeard-admin.js:157 -msgid "Abort" -msgstr "Annuleren" - -#: static/treebeard/treebeard-admin.js:172 -msgid "As Sibling" -msgstr "Als naastliggend onderdeel" - -#: static/treebeard/treebeard-admin.js:190 -msgid "As child" -msgstr "Als subonderdeel" diff --git a/wagtail/vendor/django-treebeard/treebeard/locale/pl/LC_MESSAGES/django.mo b/wagtail/vendor/django-treebeard/treebeard/locale/pl/LC_MESSAGES/django.mo deleted file mode 100644 index 160f119d1..000000000 Binary files a/wagtail/vendor/django-treebeard/treebeard/locale/pl/LC_MESSAGES/django.mo and /dev/null differ diff --git a/wagtail/vendor/django-treebeard/treebeard/locale/pl/LC_MESSAGES/django.po b/wagtail/vendor/django-treebeard/treebeard/locale/pl/LC_MESSAGES/django.po deleted file mode 100644 index be741ab79..000000000 --- a/wagtail/vendor/django-treebeard/treebeard/locale/pl/LC_MESSAGES/django.po +++ /dev/null @@ -1,44 +0,0 @@ -msgid "" -msgstr "" -"Project-Id-Version: Django-treebeard\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2010-05-03 23:53-0500\n" -"PO-Revision-Date: 2010-05-03 23:40-0500\n" -"Last-Translator: Bartosz Turkot \n" -"Language-Team: Polish\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" - -#: forms.py:16 -msgid "Child of" -msgstr "Dziecko kategorii" - -#: forms.py:17 -msgid "Sibling of" -msgstr "Sąsiad kategorii" - -#: forms.py:21 -msgid "First child of" -msgstr "Pierwsze dziecko kategorii" - -#: forms.py:22 -msgid "Before" -msgstr "Przed" - -#: forms.py:23 -msgid "After" -msgstr "Za" - -#: forms.py:26 -msgid "Position" -msgstr "Pozycja" - -#: forms.py:30 -msgid "Relative to" -msgstr "Względem" - -#: forms.py:80 -msgid "-- root --" -msgstr "-- kategoria główna --" diff --git a/wagtail/vendor/django-treebeard/treebeard/locale/ru/LC_MESSAGES/django.mo b/wagtail/vendor/django-treebeard/treebeard/locale/ru/LC_MESSAGES/django.mo deleted file mode 100644 index 5feefc1f2..000000000 Binary files a/wagtail/vendor/django-treebeard/treebeard/locale/ru/LC_MESSAGES/django.mo and /dev/null differ diff --git a/wagtail/vendor/django-treebeard/treebeard/locale/ru/LC_MESSAGES/django.po b/wagtail/vendor/django-treebeard/treebeard/locale/ru/LC_MESSAGES/django.po deleted file mode 100644 index f335c16e8..000000000 --- a/wagtail/vendor/django-treebeard/treebeard/locale/ru/LC_MESSAGES/django.po +++ /dev/null @@ -1,79 +0,0 @@ -msgid "" -msgstr "" -"Project-Id-Version: Django-treebeard\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2011-07-18 17:36+0200\n" -"PO-Revision-Date: 2009-04-10 18:37+0400\n" -"Last-Translator: chembervint \n" -"Language-Team: Russian\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\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:113 -#, python-format -msgid "Moved node \"%(node)s\" as child of \"%(other)s\"" -msgstr "" - -#: admin.py:119 -#, python-format -msgid "Moved node \"%(node)s\" as sibling of \"%(other)s\"" -msgstr "" - -#: admin.py:129 -#, python-format -msgid "Exception raised while moving node: %s" -msgstr "" - -#: al_tree.py:319 mp_tree.py:641 ns_tree.py:308 -msgid "Can't move node to a descendant." -msgstr "" - -#: forms.py:17 -msgid "Child of" -msgstr "Вложенный" - -#: forms.py:18 -msgid "Sibling of" -msgstr "Соседний к" - -#: forms.py:22 -msgid "First child of" -msgstr "Первый вложенный" - -#: forms.py:23 -msgid "Before" -msgstr "До" - -#: forms.py:24 -msgid "After" -msgstr "После" - -#: forms.py:27 -msgid "Position" -msgstr "Позиция" - -#: forms.py:31 -msgid "Relative to" -msgstr "Относительно" - -#: forms.py:81 -msgid "-- root --" -msgstr "-- корень --" - -#: mp_tree.py:521 -msgid "" -"The new node is too deep in the tree, try increasing the path.max_length " -"property and UPDATE your database" -msgstr "" - -#: mp_tree.py:702 -#, python-format -msgid "Path Overflow from: '%s'" -msgstr "" - -#: templatetags/admin_tree.py:148 -msgid "Return to ordered tree" -msgstr "" diff --git a/wagtail/vendor/django-treebeard/treebeard/locale/ru/LC_MESSAGES/djangojs.mo b/wagtail/vendor/django-treebeard/treebeard/locale/ru/LC_MESSAGES/djangojs.mo deleted file mode 100644 index 4a2f52acc..000000000 Binary files a/wagtail/vendor/django-treebeard/treebeard/locale/ru/LC_MESSAGES/djangojs.mo and /dev/null differ diff --git a/wagtail/vendor/django-treebeard/treebeard/locale/ru/LC_MESSAGES/djangojs.po b/wagtail/vendor/django-treebeard/treebeard/locale/ru/LC_MESSAGES/djangojs.po deleted file mode 100644 index b02a2a141..000000000 --- a/wagtail/vendor/django-treebeard/treebeard/locale/ru/LC_MESSAGES/djangojs.po +++ /dev/null @@ -1,25 +0,0 @@ -msgid "" -msgstr "" -"Project-Id-Version: Django-treebeard\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2011-07-18 14:12+0200\n" -"PO-Revision-Date: 2011-07-18 14:12+0200\n" -"Last-Translator: \n" -"Language-Team: Russian\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\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" - -#: static/treebeard/treebeard-admin.js:157 -msgid "Abort" -msgstr "" - -#: static/treebeard/treebeard-admin.js:172 -msgid "As Sibling" -msgstr "" - -#: static/treebeard/treebeard-admin.js:190 -msgid "As child" -msgstr "" diff --git a/wagtail/vendor/django-treebeard/treebeard/models.py b/wagtail/vendor/django-treebeard/treebeard/models.py deleted file mode 100644 index e84850287..000000000 --- a/wagtail/vendor/django-treebeard/treebeard/models.py +++ /dev/null @@ -1,620 +0,0 @@ -"""Models and base API""" - -import sys -import operator - -if sys.version_info >= (3, 0): - from functools import reduce - -from django.db.models import Q -from django.db import models, transaction, router, connections - -from treebeard.exceptions import InvalidPosition, MissingNodeOrderBy - - -class Node(models.Model): - """Node class""" - - _db_connection = None - - @classmethod - def add_root(cls, **kwargs): # pragma: no cover - """ - Adds a root node to the tree. The new root node will be the new - rightmost root node. If you want to insert a root node at a specific - position, use :meth:`add_sibling` in an already existing root node - instead. - - :param \*\*kwargs: object creation data that will be passed to the - inherited Node model - - :returns: the created node object. It will be save()d by this method. - """ - raise NotImplementedError - - @classmethod - def get_foreign_keys(cls): - """ Get foreign keys and models they refer to, so we can pre-process the - data for load_bulk """ - foreign_keys = {} - for field in cls._meta.fields: - if ( - field.get_internal_type() == 'ForeignKey' and - field.name != 'parent' - ): - foreign_keys[field.name] = field.rel.to - return foreign_keys - - @classmethod - def _process_foreign_keys(cls, foreign_keys, node_data): - """ For each foreign key try to load the actual object so load_bulk - doesn't fail trying to load an int where django expects a model instance - """ - for key in foreign_keys.keys(): - if key in node_data: - node_data[key] = foreign_keys[key].objects.get( - pk=node_data[key]) - - @classmethod - def load_bulk(cls, bulk_data, parent=None, keep_ids=False): - """ - Loads a list/dictionary structure to the tree. - - - :param bulk_data: - - The data that will be loaded, the structure is a list of - dictionaries with 2 keys: - - - ``data``: will store arguments that will be passed for object - creation, and - - - ``children``: a list of dictionaries, each one has it's own - ``data`` and ``children`` keys (a recursive structure) - - - :param parent: - - The node that will receive the structure as children, if not - specified the first level of the structure will be loaded as root - nodes - - - :param keep_ids: - - If enabled, loads the nodes with the same id that are given in the - structure. Will error if there are nodes without id info or if the - ids are already used. - - - :returns: A list of the added node ids. - """ - - # tree, iterative preorder - added = [] - # stack of nodes to analize - stack = [(parent, node) for node in bulk_data[::-1]] - foreign_keys = cls.get_foreign_keys() - - while stack: - parent, node_struct = stack.pop() - # shallow copy of the data strucure so it doesn't persist... - node_data = node_struct['data'].copy() - cls._process_foreign_keys(foreign_keys, node_data) - if keep_ids: - node_data['id'] = node_struct['id'] - if parent: - node_obj = parent.add_child(**node_data) - else: - node_obj = cls.add_root(**node_data) - added.append(node_obj.pk) - if 'children' in node_struct: - # extending the stack with the current node as the parent of - # the new nodes - stack.extend([ - (node_obj, node) - for node in node_struct['children'][::-1] - ]) - transaction.commit_unless_managed() - return added - - @classmethod - def dump_bulk(cls, parent=None, keep_ids=True): # pragma: no cover - """ - Dumps a tree branch to a python data structure. - - :param parent: - - The node whose descendants will be dumped. The node itself will be - included in the dump. If not given, the entire tree will be dumped. - - :param keep_ids: - - Stores the id value (primary key) of every node. Enabled by - default. - - :returns: A python data structure, described with detail in - :meth:`load_bulk` - """ - raise NotImplementedError - - @classmethod - def get_root_nodes(cls): # pragma: no cover - """:returns: A queryset containing the root nodes in the tree.""" - raise NotImplementedError - - @classmethod - def get_first_root_node(cls): - """ - :returns: - - The first root node in the tree or ``None`` if it is empty. - """ - try: - return cls.get_root_nodes()[0] - except IndexError: - return None - - @classmethod - def get_last_root_node(cls): - """ - :returns: - - The last root node in the tree or ``None`` if it is empty. - """ - try: - return cls.get_root_nodes().reverse()[0] - except IndexError: - return None - - @classmethod - def find_problems(cls): # pragma: no cover - """Checks for problems in the tree structure.""" - raise NotImplementedError - - @classmethod - def fix_tree(cls): # pragma: no cover - """ - Solves problems that can appear when transactions are not used and - a piece of code breaks, leaving the tree in an inconsistent state. - """ - raise NotImplementedError - - @classmethod - def get_tree(cls, parent=None): - """ - :returns: - - A list of nodes ordered as DFS, including the parent. If - no parent is given, the entire tree is returned. - """ - raise NotImplementedError - - @classmethod - def get_descendants_group_count(cls, parent=None): - """ - Helper for a very common case: get a group of siblings and the number - of *descendants* (not only children) in every sibling. - - :param parent: - - The parent of the siblings to return. If no parent is given, the - root nodes will be returned. - - :returns: - - A `list` (**NOT** a Queryset) of node objects with an extra - attribute: `descendants_count`. - """ - if parent is None: - qset = cls.get_root_nodes() - else: - qset = parent.get_children() - nodes = list(qset) - for node in nodes: - node.descendants_count = node.get_descendant_count() - return nodes - - def get_depth(self): # pragma: no cover - """:returns: the depth (level) of the node""" - raise NotImplementedError - - def get_siblings(self): # pragma: no cover - """ - :returns: - - A queryset of all the node's siblings, including the node - itself. - """ - raise NotImplementedError - - def get_children(self): # pragma: no cover - """:returns: A queryset of all the node's children""" - raise NotImplementedError - - def get_children_count(self): - """:returns: The number of the node's children""" - return self.get_children().count() - - def get_descendants(self): - """ - :returns: - - A queryset of all the node's descendants, doesn't - include the node itself (some subclasses may return a list). - """ - raise NotImplementedError - - def get_descendant_count(self): - """:returns: the number of descendants of a node.""" - return self.get_descendants().count() - - def get_first_child(self): - """ - :returns: - - The leftmost node's child, or None if it has no children. - """ - try: - return self.get_children()[0] - except IndexError: - return None - - def get_last_child(self): - """ - :returns: - - The rightmost node's child, or None if it has no children. - """ - try: - return self.get_children().reverse()[0] - except IndexError: - return None - - def get_first_sibling(self): - """ - :returns: - - The leftmost node's sibling, can return the node itself if - it was the leftmost sibling. - """ - return self.get_siblings()[0] - - def get_last_sibling(self): - """ - :returns: - - The rightmost node's sibling, can return the node itself if - it was the rightmost sibling. - """ - return self.get_siblings().reverse()[0] - - def get_prev_sibling(self): - """ - :returns: - - The previous node's sibling, or None if it was the leftmost - sibling. - """ - siblings = self.get_siblings() - ids = [obj.pk for obj in siblings] - if self.pk in ids: - idx = ids.index(self.pk) - if idx > 0: - return siblings[idx - 1] - - def get_next_sibling(self): - """ - :returns: - - The next node's sibling, or None if it was the rightmost - sibling. - """ - siblings = self.get_siblings() - ids = [obj.pk for obj in siblings] - if self.pk in ids: - idx = ids.index(self.pk) - if idx < len(siblings) - 1: - return siblings[idx + 1] - - def is_sibling_of(self, node): - """ - :returns: ``True`` if the node is a sibling of another node given as an - argument, else, returns ``False`` - - :param node: - - The node that will be checked as a sibling - """ - return self.get_siblings().filter(pk=node.pk).exists() - - def is_child_of(self, node): - """ - :returns: ``True`` if the node is a child of another node given as an - argument, else, returns ``False`` - - :param node: - - The node that will be checked as a parent - """ - return node.get_children().filter(pk=self.pk).exists() - - def is_descendant_of(self, node): # pragma: no cover - """ - :returns: ``True`` if the node is a descendant of another node given - as an argument, else, returns ``False`` - - :param node: - - The node that will be checked as an ancestor - """ - raise NotImplementedError - - def add_child(self, **kwargs): # pragma: no cover - """ - Adds a child to the node. The new node will be the new rightmost - child. If you want to insert a node at a specific position, - use the :meth:`add_sibling` method of an already existing - child node instead. - - :param \*\*kwargs: - - Object creation data that will be passed to the inherited Node - model - - :returns: The created node object. It will be save()d by this method. - """ - raise NotImplementedError - - def add_sibling(self, pos=None, **kwargs): # pragma: no cover - """ - Adds a new node as a sibling to the current node object. - - - :param pos: - The position, relative to the current node object, where the - new node will be inserted, can be one of: - - - ``first-sibling``: the new node will be the new leftmost sibling - - ``left``: the new node will take the node's place, which will be - moved to the right 1 position - - ``right``: the new node will be inserted at the right of the node - - ``last-sibling``: the new node will be the new rightmost sibling - - ``sorted-sibling``: the new node will be at the right position - according to the value of node_order_by - - :param \*\*kwargs: - - Object creation data that will be passed to the inherited - Node model - - :returns: - - The created node object. It will be saved by this method. - - :raise InvalidPosition: when passing an invalid ``pos`` parm - :raise InvalidPosition: when :attr:`node_order_by` is enabled and the - ``pos`` parm wasn't ``sorted-sibling`` - :raise MissingNodeOrderBy: when passing ``sorted-sibling`` as ``pos`` - and the :attr:`node_order_by` attribute is missing - """ - raise NotImplementedError - - def get_root(self): # pragma: no cover - """:returns: the root node for the current node object.""" - raise NotImplementedError - - def is_root(self): - """:returns: True if the node is a root node (else, returns False)""" - return self.get_root().pk == self.pk - - def is_leaf(self): - """:returns: True if the node is a leaf node (else, returns False)""" - return not self.get_children().exists() - - def get_ancestors(self): # pragma: no cover - """ - :returns: - - A queryset containing the current node object's ancestors, - starting by the root node and descending to the parent. - (some subclasses may return a list) - """ - raise NotImplementedError - - def get_parent(self, update=False): # pragma: no cover - """ - :returns: the parent node of the current node object. - Caches the result in the object itself to help in loops. - - :param update: Updates de cached value. - """ - raise NotImplementedError - - def move(self, target, pos=None): # pragma: no cover - """ - Moves the current node and all it's descendants to a new position - relative to another node. - - :param target: - - The node that will be used as a relative child/sibling when moving - - :param pos: - - The position, relative to the target node, where the - current node object will be moved to, can be one of: - - - ``first-child``: the node will be the new leftmost child of the - ``target`` node - - ``last-child``: the node will be the new rightmost child of the - ``target`` node - - ``sorted-child``: the new node will be moved as a child of the - ``target`` node according to the value of :attr:`node_order_by` - - ``first-sibling``: the node will be the new leftmost sibling of - the ``target`` node - - ``left``: the node will take the ``target`` node's place, which - will be moved to the right 1 position - - ``right``: the node will be moved to the right of the ``target`` - node - - ``last-sibling``: the node will be the new rightmost sibling of - the ``target`` node - - ``sorted-sibling``: the new node will be moved as a sibling of - the ``target`` node according to the value of - :attr:`node_order_by` - - .. note:: - - If no ``pos`` is given the library will use ``last-sibling``, - or ``sorted-sibling`` if :attr:`node_order_by` is enabled. - - :returns: None - - :raise InvalidPosition: when passing an invalid ``pos`` parm - :raise InvalidPosition: when :attr:`node_order_by` is enabled and the - ``pos`` parm wasn't ``sorted-sibling`` or ``sorted-child`` - :raise InvalidMoveToDescendant: when trying to move a node to one of - it's own descendants - :raise PathOverflow: when the library can't make room for the - node's new position - :raise MissingNodeOrderBy: when passing ``sorted-sibling`` or - ``sorted-child`` as ``pos`` and the :attr:`node_order_by` - attribute is missing - """ - raise NotImplementedError - - def delete(self): - """Removes a node and all it's descendants.""" - self.__class__.objects.filter(id=self.pk).delete() - - def _prepare_pos_var(self, pos, method_name, valid_pos, valid_sorted_pos): - if pos is None: - if self.node_order_by: - pos = 'sorted-sibling' - else: - pos = 'last-sibling' - if pos not in valid_pos: - raise InvalidPosition('Invalid relative position: %s' % (pos, )) - if self.node_order_by and pos not in valid_sorted_pos: - raise InvalidPosition( - 'Must use %s in %s when node_order_by is enabled' % ( - ' or '.join(valid_sorted_pos), method_name)) - if pos in valid_sorted_pos and not self.node_order_by: - raise MissingNodeOrderBy('Missing node_order_by attribute.') - return pos - - _valid_pos_for_add_sibling = ('first-sibling', 'left', 'right', - 'last-sibling', 'sorted-sibling') - _valid_pos_for_sorted_add_sibling = ('sorted-sibling',) - - def _prepare_pos_var_for_add_sibling(self, pos): - return self._prepare_pos_var( - pos, - 'add_sibling', - self._valid_pos_for_add_sibling, - self._valid_pos_for_sorted_add_sibling) - - _valid_pos_for_move = _valid_pos_for_add_sibling + ( - 'first-child', 'last-child', 'sorted-child') - _valid_pos_for_sorted_move = _valid_pos_for_sorted_add_sibling + ( - 'sorted-child',) - - def _prepare_pos_var_for_move(self, pos): - return self._prepare_pos_var( - pos, - 'move', - self._valid_pos_for_move, - self._valid_pos_for_sorted_move) - - def get_sorted_pos_queryset(self, siblings, newobj): - """ - :returns: A queryset of the nodes that must be moved - to the right. Called only for Node models with :attr:`node_order_by` - - This function is based on _insertion_target_filters from django-mptt - (BSD licensed) by Jonathan Buchanan: - https://github.com/django-mptt/django-mptt/blob/0.3.0/mptt/signals.py - """ - - fields, filters = [], [] - for field in self.node_order_by: - value = getattr(newobj, field) - filters.append( - Q( - *[Q(**{f: v}) for f, v in fields] + - [Q(**{'%s__gt' % field: value})] - ) - ) - fields.append((field, value)) - return siblings.filter(reduce(operator.or_, filters)) - - @classmethod - def get_annotated_list(cls, parent=None): - """ - Gets an annotated list from a tree branch. - - :param parent: - - The node whose descendants will be annotated. The node itself - will be included in the list. If not given, the entire tree - will be annotated. - """ - - result, info = [], {} - start_depth, prev_depth = (None, None) - for node in cls.get_tree(parent): - depth = node.get_depth() - if start_depth is None: - start_depth = depth - open = (depth and (prev_depth is None or depth > prev_depth)) - if prev_depth is not None and depth < prev_depth: - info['close'] = list(range(0, prev_depth - depth)) - info = {'open': open, 'close': [], 'level': depth - start_depth} - result.append((node, info,)) - prev_depth = depth - if start_depth and start_depth > 0: - info['close'] = list(range(0, prev_depth - start_depth + 1)) - return result - - @classmethod - def _get_serializable_model(cls): - """ - Returns a model with a valid _meta.local_fields (serializable). - - Basically, this means the original model, not a proxied model. - - (this is a workaround for a bug in django) - """ - current_class = cls - while current_class._meta.proxy: - current_class = current_class._meta.proxy_for_model - return current_class - - @classmethod - def _get_database_connection(cls, action): - return { - 'read': connections[router.db_for_read(cls)], - 'write': connections[router.db_for_write(cls)] - }[action] - - @classmethod - def get_database_vendor(cls, action): - """ - returns the supported database vendor used by a treebeard model when - performing read (select) or write (update, insert, delete) operations. - - :param action: - - `read` or `write` - - :returns: postgresql, mysql or sqlite - """ - return cls._get_database_connection(action).vendor - - @classmethod - def _get_database_cursor(cls, action): - return cls._get_database_connection(action).cursor() - - class Meta: - """Abstract model.""" - abstract = True diff --git a/wagtail/vendor/django-treebeard/treebeard/mp_tree.py b/wagtail/vendor/django-treebeard/treebeard/mp_tree.py deleted file mode 100644 index 03e3b3f21..000000000 --- a/wagtail/vendor/django-treebeard/treebeard/mp_tree.py +++ /dev/null @@ -1,1060 +0,0 @@ -"""Materialized Path Trees""" - -import sys -import operator - -if sys.version_info >= (3, 0): - from functools import reduce - -from django.core import serializers -from django.db import models, transaction, connection -from django.db.models import F, Q -from django.utils.translation import ugettext_noop as _ - -from treebeard.numconv import NumConv -from treebeard.models import Node -from treebeard.exceptions import InvalidMoveToDescendant, PathOverflow - - -def get_base_model_class(cls): - """ - Return the class in this model's inheritance chain which implements tree behaviour - (i.e. the one which defines the 'path' field). Necessary because a method like - get_children invoked on a multiple-table-inheritance subclass needs to perform - the query on the base class, in order to return child nodes that are not - the same subclass as that parent. - """ - return cls._meta.get_field('path').model - - -class MP_NodeQuerySet(models.query.QuerySet): - """ - Custom queryset for the tree node manager. - - Needed only for the custom delete method. - """ - - def delete(self): - """ - Custom delete method, will remove all descendant nodes to ensure a - consistent tree (no orphans) - - :returns: ``None`` - """ - # we'll have to manually run through all the nodes that are going - # to be deleted and remove nodes from the list if an ancestor is - # already getting removed, since that would be redundant - removed = {} - for node in self.order_by('depth', 'path'): - found = False - for depth in range(1, int(len(node.path) / node.steplen)): - path = node._get_basepath(node.path, depth) - if path in removed: - # we are already removing a parent of this node - # skip - found = True - break - if not found: - removed[node.path] = node - - # ok, got the minimal list of nodes to remove... - # we must also remove their children - # and update every parent node's numchild attribute - # LOTS OF FUN HERE! - parents = {} - toremove = [] - for path, node in removed.items(): - parentpath = node._get_basepath(node.path, node.depth - 1) - if parentpath: - if parentpath not in parents: - parents[parentpath] = node.get_parent(True) - parent = parents[parentpath] - if parent and parent.numchild > 0: - parent.numchild -= 1 - parent.save() - if node.is_leaf(): - toremove.append(Q(path=node.path)) - else: - toremove.append(Q(path__startswith=node.path)) - - # Django will handle this as a SELECT and then a DELETE of - # ids, and will deal with removing related objects - if toremove: - qset = self.model.objects.filter(reduce(operator.or_, toremove)) - super(MP_NodeQuerySet, qset).delete() - transaction.commit_unless_managed() - - -class MP_NodeManager(models.Manager): - """Custom manager for nodes in a Materialized Path tree.""" - - def get_query_set(self): - """Sets the custom queryset as the default.""" - return MP_NodeQuerySet(self.model).order_by('path') - - -class MP_AddHandler(object): - def __init__(self): - self.stmts = [] - - -class MP_ComplexAddMoveHandler(MP_AddHandler): - - def run_sql_stmts(self): - cursor = self.node_cls._get_database_cursor('write') - for sql, vals in self.stmts: - cursor.execute(sql, vals) - - def get_sql_update_numchild(self, path, incdec='inc'): - """:returns: The sql needed the numchild value of a node""" - sql = "UPDATE %s SET numchild=numchild%s1"\ - " WHERE path=%%s" % ( - connection.ops.quote_name(get_base_model_class(self.node_cls)._meta.db_table), - {'inc': '+', 'dec': '-'}[incdec]) - vals = [path] - return sql, vals - - def reorder_nodes_before_add_or_move(self, pos, newpos, newdepth, target, - siblings, oldpath=None, - movebranch=False): - """ - Handles the reordering of nodes and branches when adding/moving - nodes. - - :returns: A tuple containing the old path and the new path. - """ - if ( - (pos == 'last-sibling') or - (pos == 'right' and target == target.get_last_sibling()) - ): - # easy, the last node - last = target.get_last_sibling() - newpath = last._inc_path() - if movebranch: - self.stmts.append( - self.get_sql_newpath_in_branches(oldpath, newpath)) - else: - # do the UPDATE dance - - if newpos is None: - siblings = target.get_siblings() - siblings = {'left': siblings.filter(path__gte=target.path), - 'right': siblings.filter(path__gt=target.path), - 'first-sibling': siblings}[pos] - basenum = target._get_lastpos_in_path() - newpos = {'first-sibling': 1, - 'left': basenum, - 'right': basenum + 1}[pos] - - newpath = self.node_cls._get_path(target.path, newdepth, newpos) - - # If the move is amongst siblings and is to the left and there - # are siblings to the right of its new position then to be on - # the safe side we temporarily dump it on the end of the list - tempnewpath = None - if movebranch and len(oldpath) == len(newpath): - parentoldpath = self.node_cls._get_basepath( - oldpath, - int(len(oldpath) / self.node_cls.steplen) - 1 - ) - parentnewpath = self.node_cls._get_basepath( - newpath, newdepth - 1) - if ( - parentoldpath == parentnewpath and - siblings and - newpath < oldpath - ): - last = target.get_last_sibling() - basenum = last._get_lastpos_in_path() - tempnewpath = self.node_cls._get_path( - newpath, newdepth, basenum + 2) - self.stmts.append( - self.get_sql_newpath_in_branches( - oldpath, tempnewpath)) - - # Optimisation to only move siblings which need moving - # (i.e. if we've got holes, allow them to compress) - movesiblings = [] - priorpath = newpath - for node in siblings: - # If the path of the node is already greater than the path - # of the previous node it doesn't need shifting - if node.path > priorpath: - break - # It does need shifting, so add to the list - movesiblings.append(node) - # Calculate the path that it would be moved to, as that's - # the next "priorpath" - priorpath = node._inc_path() - movesiblings.reverse() - - for node in movesiblings: - # moving the siblings (and their branches) at the right of the - # related position one step to the right - sql, vals = self.get_sql_newpath_in_branches( - node.path, node._inc_path()) - self.stmts.append((sql, vals)) - - if movebranch: - if oldpath.startswith(node.path): - # if moving to a parent, update oldpath since we just - # increased the path of the entire branch - oldpath = vals[0] + oldpath[len(vals[0]):] - if target.path.startswith(node.path): - # and if we moved the target, update the object - # django made for us, since the update won't do it - # maybe useful in loops - target.path = vals[0] + target.path[len(vals[0]):] - if movebranch: - # node to move - if tempnewpath: - self.stmts.append( - self.get_sql_newpath_in_branches( - tempnewpath, newpath)) - else: - self.stmts.append( - self.get_sql_newpath_in_branches( - oldpath, newpath)) - return oldpath, newpath - - def get_sql_newpath_in_branches(self, oldpath, newpath): - """ - :returns: The sql needed to move a branch to another position. - - .. note:: - - The generated sql will only update the depth values if needed. - - """ - - vendor = self.node_cls.get_database_vendor('write') - sql1 = "UPDATE %s SET" % ( - connection.ops.quote_name(get_base_model_class(self.node_cls)._meta.db_table), ) - - # <3 "standard" sql - if vendor == 'sqlite': - # I know that the third argument in SUBSTR (LENGTH(path)) is - # awful, but sqlite fails without it: - # OperationalError: wrong number of arguments to function substr() - # even when the documentation says that 2 arguments are valid: - # http://www.sqlite.org/lang_corefunc.html - sqlpath = "%s||SUBSTR(path, %s, LENGTH(path))" - elif vendor == 'mysql': - # hooray for mysql ignoring standards in their default - # configuration! - # to make || work as it should, enable ansi mode - # http://dev.mysql.com/doc/refman/5.0/en/ansi-mode.html - sqlpath = "CONCAT(%s, SUBSTR(path, %s))" - else: - sqlpath = "%s||SUBSTR(path, %s)" - - sql2 = ["path=%s" % (sqlpath, )] - vals = [newpath, len(oldpath) + 1] - if len(oldpath) != len(newpath) and vendor != 'mysql': - # when using mysql, this won't update the depth and it has to be - # done in another query - # doesn't even work with sql_mode='ANSI,TRADITIONAL' - # TODO: FIND OUT WHY?!?? right now I'm just blaming mysql - sql2.append("depth=LENGTH(%s)/%%s" % (sqlpath, )) - vals.extend([newpath, len(oldpath) + 1, self.node_cls.steplen]) - sql3 = "WHERE path LIKE %s" - vals.extend([oldpath + '%']) - sql = '%s %s %s' % (sql1, ', '.join(sql2), sql3) - return sql, vals - - -class MP_AddRootHandler(MP_AddHandler): - def __init__(self, cls, instance, **kwargs): - super(MP_AddRootHandler, self).__init__() - self.cls = cls - self.instance = instance - self.kwargs = kwargs - - def process(self): - - # do we have a root node already? - last_root = self.cls.get_last_root_node() - - if last_root and last_root.node_order_by: - # there are root nodes and node_order_by has been set - # delegate sorted insertion to add_sibling - return last_root.add_sibling('sorted-sibling', self.instance, **self.kwargs) - - if last_root: - # adding the new root node as the last one - newpath = last_root._inc_path() - else: - # adding the first root node - newpath = self.cls._get_path(None, 1, 1) - - if self.instance: - if self.instance.pk: - raise ValueError("Attempted to add a tree node that is already in the database") - newobj = self.instance - else: - # creating the new object - newobj = self.cls(**self.kwargs) - - newobj.depth = 1 - newobj.path = newpath - # saving the instance before returning it - newobj.save() - transaction.commit_unless_managed() - return newobj - - -class MP_AddChildHandler(MP_AddHandler): - def __init__(self, node, instance, **kwargs): - super(MP_AddChildHandler, self).__init__() - self.node = node - self.node_cls = node.__class__ - self.instance = instance - self.kwargs = kwargs - - def process(self): - if self.node_cls.node_order_by and not self.node.is_leaf(): - # there are child nodes and node_order_by has been set - # delegate sorted insertion to add_sibling - self.node.numchild += 1 - return self.node.get_last_child().add_sibling( - 'sorted-sibling', self.instance, **self.kwargs) - - if self.instance: - if self.instance.pk: - raise ValueError("Attempted to add a tree node that is already in the database") - newobj = self.instance - else: - # creating a new object - newobj = self.node_cls(**self.kwargs) - - newobj.depth = self.node.depth + 1 - if self.node.is_leaf(): - # the node had no children, adding the first child - newobj.path = self.node_cls._get_path( - self.node.path, newobj.depth, 1) - max_length = self.node_cls._meta.get_field('path').max_length - if len(newobj.path) > max_length: - raise PathOverflow( - _('The new node is too deep in the tree, try' - ' increasing the path.max_length property' - ' and UPDATE your database')) - else: - # adding the new child as the last one - newobj.path = self.node.get_last_child()._inc_path() - # saving the instance before returning it - newobj.save() - newobj._cached_parent_obj = self.node - - get_base_model_class(self.node_cls).objects.filter( - path=self.node.path).update(numchild=F('numchild')+1) - - # we increase the numchild value of the object in memory - self.node.numchild += 1 - transaction.commit_unless_managed() - return newobj - - -class MP_AddSiblingHandler(MP_ComplexAddMoveHandler): - def __init__(self, node, pos, instance, **kwargs): - super(MP_AddSiblingHandler, self).__init__() - self.node = node - self.node_cls = node.__class__ - self.pos = pos - self.instance = instance - self.kwargs = kwargs - - def process(self): - self.pos = self.node._prepare_pos_var_for_add_sibling(self.pos) - - if self.instance: - if self.instance.pk: - raise ValueError("Attempted to add a tree node that is already in the database") - newobj = self.instance - else: - # creating a new object - newobj = self.node_cls(**self.kwargs) - - newobj.depth = self.node.depth - - if self.pos == 'sorted-sibling': - siblings = self.node.get_sorted_pos_queryset( - self.node.get_siblings(), newobj) - try: - newpos = siblings.all()[0]._get_lastpos_in_path() - except IndexError: - newpos = None - if newpos is None: - self.pos = 'last-sibling' - else: - newpos, siblings = None, [] - - _, newpath = self.reorder_nodes_before_add_or_move( - self.pos, newpos, self.node.depth, self.node, siblings, None, - False) - - parentpath = self.node._get_basepath(newpath, self.node.depth - 1) - if parentpath: - self.stmts.append( - self.get_sql_update_numchild(parentpath, 'inc')) - - self.run_sql_stmts() - - # saving the instance before returning it - newobj.path = newpath - newobj.save() - - transaction.commit_unless_managed() - return newobj - - -class MP_MoveHandler(MP_ComplexAddMoveHandler): - def __init__(self, node, target, pos=None): - super(MP_MoveHandler, self).__init__() - self.node = node - self.node_cls = node.__class__ - self.target = target - self.pos = pos - - def process(self): - - self.pos = self.node._prepare_pos_var_for_move(self.pos) - - oldpath = self.node.path - - # initialize variables and if moving to a child, updates "move to - # child" to become a "move to sibling" if possible (if it can't - # be done, it means that we are adding the first child) - newdepth, siblings, newpos = self.update_move_to_child_vars() - - if self.target.is_descendant_of(self.node): - raise InvalidMoveToDescendant( - _("Can't move node to a descendant.")) - - if ( - oldpath == self.target.path and - ( - (self.pos == 'left') or - ( - self.pos in ('right', 'last-sibling') and - self.target.path == self.target.get_last_sibling().path - ) or - ( - self.pos == 'first-sibling' and - self.target.path == self.target.get_first_sibling().path - ) - ) - ): - # special cases, not actually moving the node so no need to UPDATE - return - - if self.pos == 'sorted-sibling': - siblings = self.node.get_sorted_pos_queryset( - self.target.get_siblings(), self.node) - try: - newpos = siblings.all()[0]._get_lastpos_in_path() - except IndexError: - newpos = None - if newpos is None: - self.pos = 'last-sibling' - - # generate the sql that will do the actual moving of nodes - oldpath, newpath = self.reorder_nodes_before_add_or_move( - self.pos, newpos, newdepth, self.target, siblings, oldpath, True) - # updates needed for mysql and children count in parents - self.sanity_updates_after_move(oldpath, newpath) - - self.run_sql_stmts() - transaction.commit_unless_managed() - - def sanity_updates_after_move(self, oldpath, newpath): - """ - Updates the list of sql statements needed after moving nodes. - - 1. :attr:`depth` updates *ONLY* needed by mysql databases (*sigh*) - 2. update the number of children of parent nodes - """ - if ( - self.node_cls.get_database_vendor('write') == 'mysql' and - len(oldpath) != len(newpath) - ): - # no words can describe how dumb mysql is - # we must update the depth of the branch in a different query - self.stmts.append( - self.get_mysql_update_depth_in_branch(newpath)) - - oldparentpath = self.node_cls._get_parent_path_from_path(oldpath) - newparentpath = self.node_cls._get_parent_path_from_path(newpath) - if ( - (not oldparentpath and newparentpath) or - (oldparentpath and not newparentpath) or - (oldparentpath != newparentpath) - ): - # node changed parent, updating count - if oldparentpath: - self.stmts.append( - self.get_sql_update_numchild(oldparentpath, 'dec')) - if newparentpath: - self.stmts.append( - self.get_sql_update_numchild(newparentpath, 'inc')) - - def update_move_to_child_vars(self): - """Update preliminar vars in :meth:`move` when moving to a child""" - newdepth = self.target.depth - newpos = None - siblings = [] - if self.pos in ('first-child', 'last-child', 'sorted-child'): - # moving to a child - parent = self.target - newdepth += 1 - if self.target.is_leaf(): - # moving as a target's first child - newpos = 1 - self.pos = 'first-sibling' - siblings = get_base_model_class(self.node_cls).objects.none() - else: - self.target = self.target.get_last_child() - self.pos = { - 'first-child': 'first-sibling', - 'last-child': 'last-sibling', - 'sorted-child': 'sorted-sibling'}[self.pos] - - # this is not for save(), since if needed, will be handled with a - # custom UPDATE, this is only here to update django's object, - # should be useful in loops - parent.numchild += 1 - - return newdepth, siblings, newpos - - def get_mysql_update_depth_in_branch(self, path): - """ - :returns: The sql needed to update the depth of all the nodes in a - branch. - """ - sql = "UPDATE %s SET depth=LENGTH(path)/%%s WHERE path LIKE %%s" % ( - connection.ops.quote_name(get_base_model_class(self.node_cls)._meta.db_table), ) - vals = [self.node_cls.steplen, path + '%'] - return sql, vals - - -class MP_Node(Node): - """Abstract model to create your own Materialized Path Trees.""" - - steplen = 4 - alphabet = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ' - node_order_by = [] - path = models.CharField(max_length=255, unique=True) - depth = models.PositiveIntegerField() - numchild = models.PositiveIntegerField(default=0) - gap = 1 - - objects = MP_NodeManager() - - numconv_obj_ = None - - @classmethod - def _int2str(cls, num): - return cls.numconv_obj().int2str(num) - - @classmethod - def _str2int(cls, num): - return cls.numconv_obj().str2int(num) - - @classmethod - def numconv_obj(cls): - if cls.numconv_obj_ is None: - cls.numconv_obj_ = NumConv(len(cls.alphabet), cls.alphabet) - return cls.numconv_obj_ - - @classmethod - def add_root(cls, instance=None, **kwargs): - """ - Adds a root node to the tree. - - :raise PathOverflow: when no more root objects can be added - """ - return MP_AddRootHandler(cls, instance, **kwargs).process() - - @classmethod - def dump_bulk(cls, parent=None, keep_ids=True): - """Dumps a tree branch to a python data structure.""" - - cls = get_base_model_class(cls) - - # Because of fix_tree, this method assumes that the depth - # and numchild properties in the nodes can be incorrect, - # so no helper methods are used - qset = cls._get_serializable_model().objects.all() - if parent: - qset = qset.filter(path__startswith=parent.path) - ret, lnk = [], {} - for pyobj in serializers.serialize('python', qset): - # django's serializer stores the attributes in 'fields' - fields = pyobj['fields'] - path = fields['path'] - depth = int(len(path) / cls.steplen) - # this will be useless in load_bulk - del fields['depth'] - del fields['path'] - del fields['numchild'] - if 'id' in fields: - # this happens immediately after a load_bulk - del fields['id'] - - newobj = {'data': fields} - if keep_ids: - newobj['id'] = pyobj['pk'] - - if (not parent and depth == 1) or\ - (parent and len(path) == len(parent.path)): - ret.append(newobj) - else: - parentpath = cls._get_basepath(path, depth - 1) - parentobj = lnk[parentpath] - if 'children' not in parentobj: - parentobj['children'] = [] - parentobj['children'].append(newobj) - lnk[path] = newobj - return ret - - @classmethod - def find_problems(cls): - """ - Checks for problems in the tree structure, problems can occur when: - - 1. your code breaks and you get incomplete transactions (always - use transactions!) - 2. changing the ``steplen`` value in a model (you must - :meth:`dump_bulk` first, change ``steplen`` and then - :meth:`load_bulk` - - :returns: A tuple of five lists: - - 1. a list of ids of nodes with characters not found in the - ``alphabet`` - 2. a list of ids of nodes when a wrong ``path`` length - according to ``steplen`` - 3. a list of ids of orphaned nodes - 4. a list of ids of nodes with the wrong depth value for - their path - 5. a list of ids nodes that report a wrong number of children - """ - cls = get_base_model_class(cls) - - evil_chars, bad_steplen, orphans = [], [], [] - wrong_depth, wrong_numchild = [], [] - for node in cls.objects.all(): - found_error = False - for char in node.path: - if char not in cls.alphabet: - evil_chars.append(node.pk) - found_error = True - break - if found_error: - continue - if len(node.path) % cls.steplen: - bad_steplen.append(node.pk) - continue - try: - node.get_parent(True) - except cls.DoesNotExist: - orphans.append(node.pk) - continue - - if node.depth != int(len(node.path) / cls.steplen): - wrong_depth.append(node.pk) - continue - - real_numchild = cls.objects.filter( - path__range=cls._get_children_path_interval(node.path) - ).extra( - where=['LENGTH(path)/%d=%d' % (cls.steplen, node.depth + 1)] - ).count() - if real_numchild != node.numchild: - wrong_numchild.append(node.pk) - continue - - return evil_chars, bad_steplen, orphans, wrong_depth, wrong_numchild - - @classmethod - def fix_tree(cls, destructive=False): - """ - Solves some problems that can appear when transactions are not used and - a piece of code breaks, leaving the tree in an inconsistent state. - - The problems this method solves are: - - 1. Nodes with an incorrect ``depth`` or ``numchild`` values due to - incorrect code and lack of database transactions. - 2. "Holes" in the tree. This is normal if you move/delete nodes a - lot. Holes in a tree don't affect performance, - 3. Incorrect ordering of nodes when ``node_order_by`` is enabled. - Ordering is enforced on *node insertion*, so if an attribute in - ``node_order_by`` is modified after the node is inserted, the - tree ordering will be inconsistent. - - :param destructive: - - A boolean value. If True, a more agressive fix_tree method will be - attemped. If False (the default), it will use a safe (and fast!) - fix approach, but it will only solve the ``depth`` and - ``numchild`` nodes, it won't fix the tree holes or broken path - ordering. - - .. warning:: - - Currently what the ``destructive`` method does is: - - 1. Backup the tree with :meth:`dump_data` - 2. Remove all nodes in the tree. - 3. Restore the tree with :meth:`load_data` - - So, even when the primary keys of your nodes will be preserved, - this method isn't foreign-key friendly. That needs complex - in-place tree reordering, not available at the moment (hint: - patches are welcome). - """ - cls = get_base_model_class(cls) - - if destructive: - dump = cls.dump_bulk(None, True) - cls.objects.all().delete() - cls.load_bulk(dump, None, True) - else: - cursor = cls._get_database_cursor('write') - - # fix the depth field - # we need the WHERE to speed up postgres - sql = "UPDATE %s "\ - "SET depth=LENGTH(path)/%%s "\ - "WHERE depth!=LENGTH(path)/%%s" % ( - connection.ops.quote_name(cls._meta.db_table), ) - vals = [cls.steplen, cls.steplen] - cursor.execute(sql, vals) - - # fix the numchild field - vals = ['_' * cls.steplen] - # the cake and sql portability are a lie - if cls.get_database_vendor('read') == 'mysql': - sql = "SELECT tbn1.path, tbn1.numchild, ("\ - "SELECT COUNT(1) "\ - "FROM %(table)s AS tbn2 "\ - "WHERE tbn2.path LIKE "\ - "CONCAT(tbn1.path, %%s)) AS real_numchild "\ - "FROM %(table)s AS tbn1 "\ - "HAVING tbn1.numchild != real_numchild" % { - 'table': connection.ops.quote_name( - cls._meta.db_table)} - else: - subquery = "(SELECT COUNT(1) FROM %(table)s AS tbn2"\ - " WHERE tbn2.path LIKE tbn1.path||%%s)" - sql = ("SELECT tbn1.path, tbn1.numchild, " + subquery + - " FROM %(table)s AS tbn1 WHERE tbn1.numchild != " + - subquery) - sql = sql % { - 'table': connection.ops.quote_name(cls._meta.db_table)} - # we include the subquery twice - vals *= 2 - cursor.execute(sql, vals) - sql = "UPDATE %(table)s "\ - "SET numchild=%%s "\ - "WHERE path=%%s" % { - 'table': connection.ops.quote_name(cls._meta.db_table)} - for node_data in cursor.fetchall(): - vals = [node_data[2], node_data[0]] - cursor.execute(sql, vals) - - transaction.commit_unless_managed() - - @classmethod - def get_tree(cls, parent=None): - """ - :returns: - - A *queryset* of nodes ordered as DFS, including the parent. - If no parent is given, the entire tree is returned. - """ - cls = get_base_model_class(cls) # method doesn't make sense when constrained to a subclass - - if parent is None: - # return the entire tree - return cls.objects.all() - if parent.is_leaf(): - return cls.objects.filter(pk=parent.pk) - return cls.objects.filter(path__startswith=parent.path, - depth__gte=parent.depth) - - @classmethod - def get_root_nodes(cls): - """:returns: A queryset containing the root nodes in the tree.""" - return get_base_model_class(cls).objects.filter(depth=1) - - @classmethod - def get_descendants_group_count(cls, parent=None): - """ - Helper for a very common case: get a group of siblings and the number - of *descendants* in every sibling. - """ - - #~ - # disclaimer: this is the FOURTH implementation I wrote for this - # function. I really tried to make it return a queryset, but doing so - # with a *single* query isn't trivial with Django's ORM. - - # ok, I DID manage to make Django's ORM return a queryset here, - # defining two querysets, passing one subquery in the tables parameters - # of .extra() of the second queryset, using the undocumented order_by - # feature, and using a HORRIBLE hack to avoid django quoting the - # subquery as a table, BUT (and there is always a but) the hack didn't - # survive turning the QuerySet into a ValuesQuerySet, so I just used - # good old SQL. - # NOTE: in case there is interest, the hack to avoid django quoting the - # subquery as a table, was adding the subquery to the alias cache of - # the queryset's query object: - # - # qset.query.quote_cache[subquery] = subquery - # - # If there is a better way to do this in an UNMODIFIED django 1.0, let - # me know. - #~ - - cls = get_base_model_class(cls) - - if parent: - depth = parent.depth + 1 - params = cls._get_children_path_interval(parent.path) - extrand = 'AND path BETWEEN %s AND %s' - else: - depth = 1 - params = [] - extrand = '' - - sql = 'SELECT * FROM %(table)s AS t1 INNER JOIN '\ - ' (SELECT '\ - ' SUBSTR(path, 1, %(subpathlen)s) AS subpath, '\ - ' COUNT(1)-1 AS count '\ - ' FROM %(table)s '\ - ' WHERE depth >= %(depth)s %(extrand)s'\ - ' GROUP BY subpath) AS t2 '\ - ' ON t1.path=t2.subpath '\ - ' ORDER BY t1.path' % { - 'table': connection.ops.quote_name(cls._meta.db_table), - 'subpathlen': depth * cls.steplen, - 'depth': depth, - 'extrand': extrand} - cursor = cls._get_database_cursor('write') - cursor.execute(sql, params) - - ret = [] - field_names = [field[0] for field in cursor.description] - for node_data in cursor.fetchall(): - node = cls(**dict(zip(field_names, node_data[:-2]))) - node.descendants_count = node_data[-1] - ret.append(node) - transaction.commit_unless_managed() - return ret - - def get_depth(self): - """:returns: the depth (level) of the node""" - return self.depth - - def get_siblings(self): - """ - :returns: A queryset of all the node's siblings, including the node - itself. - """ - qset = get_base_model_class(self.__class__).objects.filter(depth=self.depth) - if self.depth > 1: - # making sure the non-root nodes share a parent - parentpath = self._get_basepath(self.path, self.depth - 1) - qset = qset.filter( - path__range=self._get_children_path_interval(parentpath)) - return qset - - def get_children(self): - """:returns: A queryset of all the node's children""" - if self.is_leaf(): - return get_base_model_class(self.__class__).objects.none() - return get_base_model_class(self.__class__).objects.filter( - depth=self.depth + 1, - path__range=self._get_children_path_interval(self.path) - ) - - def get_next_sibling(self): - """ - :returns: The next node's sibling, or None if it was the rightmost - sibling. - """ - try: - return self.get_siblings().filter(path__gt=self.path)[0] - except IndexError: - return None - - def get_descendants(self): - """ - :returns: A queryset of all the node's descendants as DFS, doesn't - include the node itself - """ - return self.__class__.get_tree(self).exclude(pk=self.pk) - - def get_prev_sibling(self): - """ - :returns: The previous node's sibling, or None if it was the leftmost - sibling. - """ - try: - return self.get_siblings().filter(path__lt=self.path).reverse()[0] - except IndexError: - return None - - def get_children_count(self): - """ - :returns: The number the node's children, calculated in the most - efficient possible way. - """ - return self.numchild - - def is_sibling_of(self, node): - """ - :returns: ``True`` if the node is a sibling of another node given as an - argument, else, returns ``False`` - """ - aux = self.depth == node.depth - # Check non-root nodes share a parent only if they have the same depth - if aux and self.depth > 1: - # making sure the non-root nodes share a parent - parentpath = self._get_basepath(self.path, self.depth - 1) - return aux and node.path.startswith(parentpath) - return aux - - def is_child_of(self, node): - """ - :returns: ``True`` is the node if a child of another node given as an - argument, else, returns ``False`` - """ - return (self.path.startswith(node.path) and - self.depth == node.depth + 1) - - def is_descendant_of(self, node): - """ - :returns: ``True`` if the node is a descendant of another node given - as an argument, else, returns ``False`` - """ - return self.path.startswith(node.path) and self.depth > node.depth - - def add_child(self, instance=None, **kwargs): - """ - Adds a child to the node. - - :raise PathOverflow: when no more child nodes can be added - """ - return MP_AddChildHandler(self, instance, **kwargs).process() - - def add_sibling(self, pos=None, instance=None, **kwargs): - """ - Adds a new node as a sibling to the current node object. - - :raise PathOverflow: when the library can't make room for the - node's new position - """ - return MP_AddSiblingHandler(self, pos, instance, **kwargs).process() - - def get_root(self): - """:returns: the root node for the current node object.""" - return get_base_model_class(self.__class__).objects.get(path=self.path[0:self.steplen]) - - def is_leaf(self): - """:returns: True if the node is a leaf node (else, returns False)""" - return self.numchild == 0 - - def get_ancestors(self): - """ - :returns: A queryset containing the current node object's ancestors, - starting by the root node and descending to the parent. - """ - paths = [ - self.path[0:pos] - for pos in range(0, len(self.path), self.steplen)[1:] - ] - return get_base_model_class(self.__class__).objects.filter(path__in=paths).order_by('depth') - - def get_parent(self, update=False): - """ - :returns: the parent node of the current node object. - Caches the result in the object itself to help in loops. - """ - depth = int(len(self.path) / self.steplen) - if depth <= 1: - return - try: - if update: - del self._cached_parent_obj - else: - return self._cached_parent_obj - except AttributeError: - pass - parentpath = self._get_basepath(self.path, depth - 1) - self._cached_parent_obj = get_base_model_class(self.__class__).objects.get(path=parentpath) - return self._cached_parent_obj - - def move(self, target, pos=None): - """ - Moves the current node and all it's descendants to a new position - relative to another node. - - :raise PathOverflow: when the library can't make room for the - node's new position - """ - return MP_MoveHandler(self, target, pos).process() - - @classmethod - def _get_basepath(cls, path, depth): - """:returns: The base path of another path up to a given depth""" - if path: - return path[0:depth * cls.steplen] - return '' - - @classmethod - def _get_path(cls, path, depth, newstep): - """ - Builds a path given some values - - :param path: the base path - :param depth: the depth of the node - :param newstep: the value (integer) of the new step - """ - parentpath = cls._get_basepath(path, depth - 1) - key = cls._int2str(newstep) - return '%s%s%s' % (parentpath, - '0' * (cls.steplen - len(key)), - key) - - def _inc_path(self): - """:returns: The path of the next sibling of a given node path.""" - newpos = self._str2int(self.path[-self.steplen:]) + 1 - key = self._int2str(newpos) - if len(key) > self.steplen: - raise PathOverflow(_("Path Overflow from: '%s'" % (self.path, ))) - return '%s%s%s' % (self.path[:-self.steplen], - '0' * (self.steplen - len(key)), - key) - - def _get_lastpos_in_path(self): - """:returns: The integer value of the last step in a path.""" - return self._str2int(self.path[-self.steplen:]) - - @classmethod - def _get_parent_path_from_path(cls, path): - """:returns: The parent path for a given path""" - if path: - return path[0:len(path) - cls.steplen] - return '' - - @classmethod - def _get_children_path_interval(cls, path): - """:returns: An interval of all possible children paths for a node.""" - return (path + cls.alphabet[0] * cls.steplen, - path + cls.alphabet[-1] * cls.steplen) - - class Meta: - """Abstract model.""" - abstract = True diff --git a/wagtail/vendor/django-treebeard/treebeard/ns_tree.py b/wagtail/vendor/django-treebeard/treebeard/ns_tree.py deleted file mode 100644 index 20d184f7e..000000000 --- a/wagtail/vendor/django-treebeard/treebeard/ns_tree.py +++ /dev/null @@ -1,623 +0,0 @@ -"""Nested Sets""" - -import sys -import operator - -if sys.version_info >= (3, 0): - from functools import reduce - -from django.core import serializers -from django.db import connection, models, transaction -from django.db.models import Q -from django.utils.translation import ugettext_noop as _ - -from treebeard.exceptions import InvalidMoveToDescendant -from treebeard.models import Node - - -class NS_NodeQuerySet(models.query.QuerySet): - """ - Custom queryset for the tree node manager. - - Needed only for the customized delete method. - """ - - def delete(self, removed_ranges=None): - """ - Custom delete method, will remove all descendant nodes to ensure a - consistent tree (no orphans) - - :returns: ``None`` - """ - if removed_ranges is not None: - # we already know the children, let's call the default django - # delete method and let it handle the removal of the user's - # foreign keys... - super(NS_NodeQuerySet, self).delete() - cursor = self.model._get_database_cursor('write') - - # Now closing the gap (Celko's trees book, page 62) - # We do this for every gap that was left in the tree when the nodes - # were removed. If many nodes were removed, we're going to update - # the same nodes over and over again. This would be probably - # cheaper precalculating the gapsize per intervals, or just do a - # complete reordering of the tree (uses COUNT)... - for tree_id, drop_lft, drop_rgt in sorted(removed_ranges, - reverse=True): - sql, params = self.model._get_close_gap_sql(drop_lft, drop_rgt, - tree_id) - cursor.execute(sql, params) - else: - # we'll have to manually run through all the nodes that are going - # to be deleted and remove nodes from the list if an ancestor is - # already getting removed, since that would be redundant - removed = {} - for node in self.order_by('tree_id', 'lft'): - found = False - for rid, rnode in removed.items(): - if node.is_descendant_of(rnode): - found = True - break - if not found: - removed[node.pk] = node - - # ok, got the minimal list of nodes to remove... - # we must also remove their descendants - toremove = [] - ranges = [] - for id, node in removed.items(): - toremove.append(Q(lft__range=(node.lft, node.rgt)) & - Q(tree_id=node.tree_id)) - ranges.append((node.tree_id, node.lft, node.rgt)) - if toremove: - self.model.objects.filter( - reduce(operator.or_, - toremove) - ).delete(removed_ranges=ranges) - transaction.commit_unless_managed() - - -class NS_NodeManager(models.Manager): - """Custom manager for nodes in a Nested Sets tree.""" - - def get_query_set(self): - """Sets the custom queryset as the default.""" - return NS_NodeQuerySet(self.model).order_by('tree_id', 'lft') - - -class NS_Node(Node): - """Abstract model to create your own Nested Sets Trees.""" - node_order_by = [] - - lft = models.PositiveIntegerField(db_index=True) - rgt = models.PositiveIntegerField(db_index=True) - tree_id = models.PositiveIntegerField(db_index=True) - depth = models.PositiveIntegerField(db_index=True) - - objects = NS_NodeManager() - - @classmethod - def add_root(cls, **kwargs): - """Adds a root node to the tree.""" - - # do we have a root node already? - last_root = cls.get_last_root_node() - - if last_root and last_root.node_order_by: - # there are root nodes and node_order_by has been set - # delegate sorted insertion to add_sibling - return last_root.add_sibling('sorted-sibling', **kwargs) - - if last_root: - # adding the new root node as the last one - newtree_id = last_root.tree_id + 1 - else: - # adding the first root node - newtree_id = 1 - - # creating the new object - newobj = cls(**kwargs) - newobj.depth = 1 - newobj.tree_id = newtree_id - newobj.lft = 1 - newobj.rgt = 2 - # saving the instance before returning it - newobj.save() - transaction.commit_unless_managed() - return newobj - - @classmethod - def _move_right(cls, tree_id, rgt, lftmove=False, incdec=2): - if lftmove: - lftop = '>=' - else: - lftop = '>' - sql = 'UPDATE %(table)s '\ - ' SET lft = CASE WHEN lft %(lftop)s %(parent_rgt)d '\ - ' THEN lft %(incdec)+d '\ - ' ELSE lft END, '\ - ' rgt = CASE WHEN rgt >= %(parent_rgt)d '\ - ' THEN rgt %(incdec)+d '\ - ' ELSE rgt END '\ - ' WHERE rgt >= %(parent_rgt)d AND '\ - ' tree_id = %(tree_id)s' % { - 'table': connection.ops.quote_name(cls._meta.db_table), - 'parent_rgt': rgt, - 'tree_id': tree_id, - 'lftop': lftop, - 'incdec': incdec} - return sql, [] - - @classmethod - def _move_tree_right(cls, tree_id): - sql = 'UPDATE %(table)s '\ - ' SET tree_id = tree_id+1 '\ - ' WHERE tree_id >= %(tree_id)d' % { - 'table': connection.ops.quote_name(cls._meta.db_table), - 'tree_id': tree_id} - return sql, [] - - def add_child(self, **kwargs): - """Adds a child to the node.""" - if not self.is_leaf(): - # there are child nodes, delegate insertion to add_sibling - if self.node_order_by: - pos = 'sorted-sibling' - else: - pos = 'last-sibling' - last_child = self.get_last_child() - last_child._cached_parent_obj = self - return last_child.add_sibling(pos, **kwargs) - - # we're adding the first child of this node - sql, params = self.__class__._move_right(self.tree_id, - self.rgt, False, 2) - - # creating a new object - newobj = self.__class__(**kwargs) - newobj.tree_id = self.tree_id - newobj.depth = self.depth + 1 - newobj.lft = self.lft + 1 - newobj.rgt = self.lft + 2 - - # this is just to update the cache - self.rgt += 2 - - newobj._cached_parent_obj = self - - cursor = self._get_database_cursor('write') - cursor.execute(sql, params) - - # saving the instance before returning it - newobj.save() - transaction.commit_unless_managed() - - return newobj - - def add_sibling(self, pos=None, **kwargs): - """Adds a new node as a sibling to the current node object.""" - - pos = self._prepare_pos_var_for_add_sibling(pos) - - # creating a new object - newobj = self.__class__(**kwargs) - newobj.depth = self.depth - - sql = None - target = self - - if target.is_root(): - newobj.lft = 1 - newobj.rgt = 2 - if pos == 'sorted-sibling': - siblings = list(target.get_sorted_pos_queryset( - target.get_siblings(), newobj)) - if siblings: - pos = 'left' - target = siblings[0] - else: - pos = 'last-sibling' - - last_root = target.__class__.get_last_root_node() - if ( - (pos == 'last-sibling') or - (pos == 'right' and target == last_root) - ): - newobj.tree_id = last_root.tree_id + 1 - else: - newpos = {'first-sibling': 1, - 'left': target.tree_id, - 'right': target.tree_id + 1}[pos] - sql, params = target.__class__._move_tree_right(newpos) - - newobj.tree_id = newpos - else: - newobj.tree_id = target.tree_id - - if pos == 'sorted-sibling': - siblings = list(target.get_sorted_pos_queryset( - target.get_siblings(), newobj)) - if siblings: - pos = 'left' - target = siblings[0] - else: - pos = 'last-sibling' - - if pos in ('left', 'right', 'first-sibling'): - siblings = list(target.get_siblings()) - - if pos == 'right': - if target == siblings[-1]: - pos = 'last-sibling' - else: - pos = 'left' - found = False - for node in siblings: - if found: - target = node - break - elif node == target: - found = True - if pos == 'left': - if target == siblings[0]: - pos = 'first-sibling' - if pos == 'first-sibling': - target = siblings[0] - - move_right = self.__class__._move_right - - if pos == 'last-sibling': - newpos = target.get_parent().rgt - sql, params = move_right(target.tree_id, newpos, False, 2) - elif pos == 'first-sibling': - newpos = target.lft - sql, params = move_right(target.tree_id, newpos - 1, False, 2) - elif pos == 'left': - newpos = target.lft - sql, params = move_right(target.tree_id, newpos, True, 2) - - newobj.lft = newpos - newobj.rgt = newpos + 1 - - # saving the instance before returning it - if sql: - cursor = self._get_database_cursor('write') - cursor.execute(sql, params) - newobj.save() - - transaction.commit_unless_managed() - - return newobj - - def move(self, target, pos=None): - """ - Moves the current node and all it's descendants to a new position - relative to another node. - """ - - pos = self._prepare_pos_var_for_move(pos) - cls = self.__class__ - - parent = None - - if pos in ('first-child', 'last-child', 'sorted-child'): - # moving to a child - if target.is_leaf(): - parent = target - pos = 'last-child' - else: - target = target.get_last_child() - pos = {'first-child': 'first-sibling', - 'last-child': 'last-sibling', - 'sorted-child': 'sorted-sibling'}[pos] - - if target.is_descendant_of(self): - raise InvalidMoveToDescendant( - _("Can't move node to a descendant.")) - - if self == target and ( - (pos == 'left') or - (pos in ('right', 'last-sibling') and - target == target.get_last_sibling()) or - (pos == 'first-sibling' and - target == target.get_first_sibling())): - # special cases, not actually moving the node so no need to UPDATE - return - - if pos == 'sorted-sibling': - siblings = list(target.get_sorted_pos_queryset( - target.get_siblings(), self)) - if siblings: - pos = 'left' - target = siblings[0] - else: - pos = 'last-sibling' - if pos in ('left', 'right', 'first-sibling'): - siblings = list(target.get_siblings()) - - if pos == 'right': - if target == siblings[-1]: - pos = 'last-sibling' - else: - pos = 'left' - found = False - for node in siblings: - if found: - target = node - break - elif node == target: - found = True - if pos == 'left': - if target == siblings[0]: - pos = 'first-sibling' - if pos == 'first-sibling': - target = siblings[0] - - # ok let's move this - cursor = self._get_database_cursor('write') - move_right = cls._move_right - gap = self.rgt - self.lft + 1 - sql = None - target_tree = target.tree_id - - # first make a hole - if pos == 'last-child': - newpos = parent.rgt - sql, params = move_right(target.tree_id, newpos, False, gap) - elif target.is_root(): - newpos = 1 - if pos == 'last-sibling': - target_tree = target.get_siblings().reverse()[0].tree_id + 1 - elif pos == 'first-sibling': - target_tree = 1 - sql, params = cls._move_tree_right(1) - elif pos == 'left': - sql, params = cls._move_tree_right(target.tree_id) - else: - if pos == 'last-sibling': - newpos = target.get_parent().rgt - sql, params = move_right(target.tree_id, newpos, False, gap) - elif pos == 'first-sibling': - newpos = target.lft - sql, params = move_right(target.tree_id, - newpos - 1, False, gap) - elif pos == 'left': - newpos = target.lft - sql, params = move_right(target.tree_id, newpos, True, gap) - - if sql: - cursor.execute(sql, params) - - # we reload 'self' because lft/rgt may have changed - - fromobj = cls.objects.get(pk=self.pk) - - depthdiff = target.depth - fromobj.depth - if parent: - depthdiff += 1 - - # move the tree to the hole - sql = "UPDATE %(table)s "\ - " SET tree_id = %(target_tree)d, "\ - " lft = lft + %(jump)d , "\ - " rgt = rgt + %(jump)d , "\ - " depth = depth + %(depthdiff)d "\ - " WHERE tree_id = %(from_tree)d AND "\ - " lft BETWEEN %(fromlft)d AND %(fromrgt)d" % { - 'table': connection.ops.quote_name(cls._meta.db_table), - 'from_tree': fromobj.tree_id, - 'target_tree': target_tree, - 'jump': newpos - fromobj.lft, - 'depthdiff': depthdiff, - 'fromlft': fromobj.lft, - 'fromrgt': fromobj.rgt} - cursor.execute(sql, []) - - # close the gap - sql, params = cls._get_close_gap_sql(fromobj.lft, - fromobj.rgt, fromobj.tree_id) - cursor.execute(sql, params) - - transaction.commit_unless_managed() - - @classmethod - def _get_close_gap_sql(cls, drop_lft, drop_rgt, tree_id): - sql = 'UPDATE %(table)s '\ - ' SET lft = CASE '\ - ' WHEN lft > %(drop_lft)d '\ - ' THEN lft - %(gapsize)d '\ - ' ELSE lft END, '\ - ' rgt = CASE '\ - ' WHEN rgt > %(drop_lft)d '\ - ' THEN rgt - %(gapsize)d '\ - ' ELSE rgt END '\ - ' WHERE (lft > %(drop_lft)d '\ - ' OR rgt > %(drop_lft)d) AND '\ - ' tree_id=%(tree_id)d' % { - 'table': connection.ops.quote_name(cls._meta.db_table), - 'gapsize': drop_rgt - drop_lft + 1, - 'drop_lft': drop_lft, - 'tree_id': tree_id} - return sql, [] - - @classmethod - def load_bulk(cls, bulk_data, parent=None, keep_ids=False): - """Loads a list/dictionary structure to the tree.""" - - # tree, iterative preorder - added = [] - if parent: - parent_id = parent.pk - else: - parent_id = None - # stack of nodes to analize - stack = [(parent_id, node) for node in bulk_data[::-1]] - foreign_keys = cls.get_foreign_keys() - while stack: - parent_id, node_struct = stack.pop() - # shallow copy of the data strucure so it doesn't persist... - node_data = node_struct['data'].copy() - cls._process_foreign_keys(foreign_keys, node_data) - if keep_ids: - node_data['id'] = node_struct['id'] - if parent_id: - parent = cls.objects.get(pk=parent_id) - node_obj = parent.add_child(**node_data) - else: - node_obj = cls.add_root(**node_data) - added.append(node_obj.pk) - if 'children' in node_struct: - # extending the stack with the current node as the parent of - # the new nodes - stack.extend([ - (node_obj.pk, node) - for node in node_struct['children'][::-1] - ]) - transaction.commit_unless_managed() - return added - - def get_children(self): - """:returns: A queryset of all the node's children""" - return self.get_descendants().filter(depth=self.depth + 1) - - def get_depth(self): - """:returns: the depth (level) of the node""" - return self.depth - - def is_leaf(self): - """:returns: True if the node is a leaf node (else, returns False)""" - return self.rgt - self.lft == 1 - - def get_root(self): - """:returns: the root node for the current node object.""" - if self.lft == 1: - return self - return self.__class__.objects.get(tree_id=self.tree_id, lft=1) - - def is_root(self): - """:returns: True if the node is a root node (else, returns False)""" - return self.lft == 1 - - def get_siblings(self): - """ - :returns: A queryset of all the node's siblings, including the node - itself. - """ - if self.lft == 1: - return self.get_root_nodes() - return self.get_parent(True).get_children() - - @classmethod - def dump_bulk(cls, parent=None, keep_ids=True): - """Dumps a tree branch to a python data structure.""" - qset = cls._get_serializable_model().get_tree(parent) - ret, lnk = [], {} - for pyobj in qset: - serobj = serializers.serialize('python', [pyobj])[0] - # django's serializer stores the attributes in 'fields' - fields = serobj['fields'] - depth = fields['depth'] - # this will be useless in load_bulk - del fields['lft'] - del fields['rgt'] - del fields['depth'] - del fields['tree_id'] - if 'id' in fields: - # this happens immediately after a load_bulk - del fields['id'] - - newobj = {'data': fields} - if keep_ids: - newobj['id'] = serobj['pk'] - - if (not parent and depth == 1) or\ - (parent and depth == parent.depth): - ret.append(newobj) - else: - parentobj = pyobj.get_parent() - parentser = lnk[parentobj.pk] - if 'children' not in parentser: - parentser['children'] = [] - parentser['children'].append(newobj) - lnk[pyobj.pk] = newobj - return ret - - @classmethod - def get_tree(cls, parent=None): - """ - :returns: - - A *queryset* of nodes ordered as DFS, including the parent. - If no parent is given, all trees are returned. - """ - if parent is None: - # return the entire tree - return cls.objects.all() - if parent.is_leaf(): - return cls.objects.filter(pk=parent.pk) - return cls.objects.filter( - tree_id=parent.tree_id, - lft__range=(parent.lft, parent.rgt - 1)) - - def get_descendants(self): - """ - :returns: A queryset of all the node's descendants as DFS, doesn't - include the node itself - """ - if self.is_leaf(): - return self.__class__.objects.none() - return self.__class__.get_tree(self).exclude(pk=self.pk) - - def get_descendant_count(self): - """:returns: the number of descendants of a node.""" - return (self.rgt - self.lft - 1) / 2 - - def get_ancestors(self): - """ - :returns: A queryset containing the current node object's ancestors, - starting by the root node and descending to the parent. - """ - if self.is_root(): - return self.__class__.objects.none() - return self.__class__.objects.filter( - tree_id=self.tree_id, - lft__lt=self.lft, - rgt__gt=self.rgt) - - def is_descendant_of(self, node): - """ - :returns: ``True`` if the node if a descendant of another node given - as an argument, else, returns ``False`` - """ - return ( - self.tree_id == node.tree_id and - self.lft > node.lft and - self.rgt < node.rgt - ) - - def get_parent(self, update=False): - """ - :returns: the parent node of the current node object. - Caches the result in the object itself to help in loops. - """ - if self.is_root(): - return - try: - if update: - del self._cached_parent_obj - else: - return self._cached_parent_obj - except AttributeError: - pass - # parent = our most direct ancestor - self._cached_parent_obj = self.get_ancestors().reverse()[0] - return self._cached_parent_obj - - @classmethod - def get_root_nodes(cls): - """:returns: A queryset containing the root nodes in the tree.""" - return cls.objects.filter(lft=1) - - class Meta: - """Abstract model.""" - abstract = True diff --git a/wagtail/vendor/django-treebeard/treebeard/numconv.py b/wagtail/vendor/django-treebeard/treebeard/numconv.py deleted file mode 100644 index 4c8d81faa..000000000 --- a/wagtail/vendor/django-treebeard/treebeard/numconv.py +++ /dev/null @@ -1,115 +0,0 @@ -"""Convert strings to numbers and numbers to strings. - -Gustavo Picon -https://tabo.pe/projects/numconv/ - -""" - - -__version__ = '2.1.1' - -# from april fool's rfc 1924 -BASE85 = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz' \ - '!#$%&()*+-;<=>?@^_`{|}~' - -# rfc4648 alphabets -BASE16 = BASE85[:16] -BASE32 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567' -BASE32HEX = BASE85[:32] -BASE64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' -BASE64URL = BASE64[:62] + '-_' - -# http://en.wikipedia.org/wiki/Base_62 useful for url shorteners -BASE62 = BASE85[:62] - - -class NumConv(object): - """Class to create converter objects. - - :param radix: The base that will be used in the conversions. - The default value is 10 for decimal conversions. - :param alphabet: A string that will be used as a encoding alphabet. - The length of the alphabet can be longer than the radix. In this - case the alphabet will be internally truncated. - - The default value is :data:`numconv.BASE85` - - :raise TypeError: when *radix* isn't an integer - :raise ValueError: when *radix* is invalid - :raise ValueError: when *alphabet* has duplicated characters - """ - - def __init__(self, radix=10, alphabet=BASE85): - """basic validation and cached_map storage""" - if int(radix) != radix: - raise TypeError('radix must be an integer') - if not 2 <= radix <= len(alphabet): - raise ValueError('radix must be >= 2 and <= %d' % ( - len(alphabet), )) - self.radix = radix - self.alphabet = alphabet - self.cached_map = dict(zip(self.alphabet, range(len(self.alphabet)))) - if len(self.cached_map) != len(self.alphabet): - raise ValueError("duplicate characters found in '%s'" % ( - self.alphabet, )) - - def int2str(self, num): - """Converts an integer into a string. - - :param num: A numeric value to be converted to another base as a - string. - - :rtype: string - - :raise TypeError: when *num* isn't an integer - :raise ValueError: when *num* isn't positive - """ - if int(num) != num: - raise TypeError('number must be an integer') - if num < 0: - raise ValueError('number must be positive') - radix, alphabet = self.radix, self.alphabet - if radix in (8, 10, 16) and \ - alphabet[:radix].lower() == BASE85[:radix].lower(): - return ({8: '%o', 10: '%d', 16: '%x'}[radix] % num).upper() - ret = '' - while True: - ret = alphabet[num % radix] + ret - if num < radix: - break - num //= radix - return ret - - def str2int(self, num): - """Converts a string into an integer. - - If possible, the built-in python conversion will be used for speed - purposes. - - :param num: A string that will be converted to an integer. - - :rtype: integer - - :raise ValueError: when *num* is invalid - """ - radix, alphabet = self.radix, self.alphabet - if radix <= 36 and alphabet[:radix].lower() == BASE85[:radix].lower(): - return int(num, radix) - ret = 0 - lalphabet = alphabet[:radix] - for char in num: - if char not in lalphabet: - raise ValueError("invalid literal for radix2int() with radix " - "%d: '%s'" % (radix, num)) - ret = ret * radix + self.cached_map[char] - return ret - - -def int2str(num, radix=10, alphabet=BASE85): - """helper function for quick base conversions from integers to strings""" - return NumConv(radix, alphabet).int2str(num) - - -def str2int(num, radix=10, alphabet=BASE85): - """helper function for quick base conversions from strings to integers""" - return NumConv(radix, alphabet).str2int(num) diff --git a/wagtail/vendor/django-treebeard/treebeard/static/treebeard/expand-collapse.png b/wagtail/vendor/django-treebeard/treebeard/static/treebeard/expand-collapse.png deleted file mode 100644 index 2f9da7e11..000000000 Binary files a/wagtail/vendor/django-treebeard/treebeard/static/treebeard/expand-collapse.png and /dev/null differ diff --git a/wagtail/vendor/django-treebeard/treebeard/static/treebeard/jquery-ui-1.8.5.custom.min.js b/wagtail/vendor/django-treebeard/treebeard/static/treebeard/jquery-ui-1.8.5.custom.min.js deleted file mode 100644 index 1461652d9..000000000 --- a/wagtail/vendor/django-treebeard/treebeard/static/treebeard/jquery-ui-1.8.5.custom.min.js +++ /dev/null @@ -1,1816 +0,0 @@ -/*! - * jQuery UI 1.8.5 - * - * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI - */ -(function (c, j) { - function k(a) { - return!c(a).parents().andSelf().filter(function () { - return c.curCSS(this, "visibility") === "hidden" || c.expr.filters.hidden(this) - }).length - } - - c.ui = c.ui || {}; - if (!c.ui.version) { - c.extend(c.ui, {version: "1.8.5", keyCode: {ALT: 18, BACKSPACE: 8, CAPS_LOCK: 20, COMMA: 188, COMMAND: 91, COMMAND_LEFT: 91, COMMAND_RIGHT: 93, CONTROL: 17, DELETE: 46, DOWN: 40, END: 35, ENTER: 13, ESCAPE: 27, HOME: 36, INSERT: 45, LEFT: 37, MENU: 93, NUMPAD_ADD: 107, NUMPAD_DECIMAL: 110, NUMPAD_DIVIDE: 111, NUMPAD_ENTER: 108, NUMPAD_MULTIPLY: 106, - NUMPAD_SUBTRACT: 109, PAGE_DOWN: 34, PAGE_UP: 33, PERIOD: 190, RIGHT: 39, SHIFT: 16, SPACE: 32, TAB: 9, UP: 38, WINDOWS: 91}}); - c.fn.extend({_focus: c.fn.focus, focus: function (a, b) { - return typeof a === "number" ? this.each(function () { - var d = this; - setTimeout(function () { - c(d).focus(); - b && b.call(d) - }, a) - }) : this._focus.apply(this, arguments) - }, scrollParent: function () { - var a; - a = c.browser.msie && /(static|relative)/.test(this.css("position")) || /absolute/.test(this.css("position")) ? this.parents().filter(function () { - return/(relative|absolute|fixed)/.test(c.curCSS(this, - "position", 1)) && /(auto|scroll)/.test(c.curCSS(this, "overflow", 1) + c.curCSS(this, "overflow-y", 1) + c.curCSS(this, "overflow-x", 1)) - }).eq(0) : this.parents().filter(function () { - return/(auto|scroll)/.test(c.curCSS(this, "overflow", 1) + c.curCSS(this, "overflow-y", 1) + c.curCSS(this, "overflow-x", 1)) - }).eq(0); - return/fixed/.test(this.css("position")) || !a.length ? c(document) : a - }, zIndex: function (a) { - if (a !== j)return this.css("zIndex", a); - if (this.length) { - a = c(this[0]); - for (var b; a.length && a[0] !== document;) { - b = a.css("position"); - if (b === "absolute" || b === "relative" || b === "fixed") { - b = parseInt(a.css("zIndex")); - if (!isNaN(b) && b != 0)return b - } - a = a.parent() - } - } - return 0 - }, disableSelection: function () { - return this.bind("mousedown.ui-disableSelection selectstart.ui-disableSelection", function (a) { - a.preventDefault() - }) - }, enableSelection: function () { - return this.unbind(".ui-disableSelection") - }}); - c.each(["Width", "Height"], function (a, b) { - function d(f, g, l, m) { - c.each(e, function () { - g -= parseFloat(c.curCSS(f, "padding" + this, true)) || 0; - if (l)g -= parseFloat(c.curCSS(f, - "border" + this + "Width", true)) || 0; - if (m)g -= parseFloat(c.curCSS(f, "margin" + this, true)) || 0 - }); - return g - } - - var e = b === "Width" ? ["Left", "Right"] : ["Top", "Bottom"], h = b.toLowerCase(), i = {innerWidth: c.fn.innerWidth, innerHeight: c.fn.innerHeight, outerWidth: c.fn.outerWidth, outerHeight: c.fn.outerHeight}; - c.fn["inner" + b] = function (f) { - if (f === j)return i["inner" + b].call(this); - return this.each(function () { - c.style(this, h, d(this, f) + "px") - }) - }; - c.fn["outer" + b] = function (f, g) { - if (typeof f !== "number")return i["outer" + b].call(this, f); - return this.each(function () { - c.style(this, - h, d(this, f, true, g) + "px") - }) - } - }); - c.extend(c.expr[":"], {data: function (a, b, d) { - return!!c.data(a, d[3]) - }, focusable: function (a) { - var b = a.nodeName.toLowerCase(), d = c.attr(a, "tabindex"); - if ("area" === b) { - b = a.parentNode; - d = b.name; - if (!a.href || !d || b.nodeName.toLowerCase() !== "map")return false; - a = c("img[usemap=#" + d + "]")[0]; - return!!a && k(a) - } - return(/input|select|textarea|button|object/.test(b) ? !a.disabled : "a" == b ? a.href || !isNaN(d) : !isNaN(d)) && k(a) - }, tabbable: function (a) { - var b = c.attr(a, "tabindex"); - return(isNaN(b) || b >= 0) && c(a).is(":focusable") - }}); - c(function () { - var a = document.createElement("div"), b = document.body; - c.extend(a.style, {minHeight: "100px", height: "auto", padding: 0, borderWidth: 0}); - c.support.minHeight = b.appendChild(a).offsetHeight === 100; - b.removeChild(a).style.display = "none" - }); - c.extend(c.ui, {plugin: {add: function (a, b, d) { - a = c.ui[a].prototype; - for (var e in d) { - a.plugins[e] = a.plugins[e] || []; - a.plugins[e].push([b, d[e]]) - } - }, call: function (a, b, d) { - if ((b = a.plugins[b]) && a.element[0].parentNode)for (var e = 0; e < b.length; e++)a.options[b[e][0]] && b[e][1].apply(a.element, - d) - }}, contains: function (a, b) { - return document.compareDocumentPosition ? a.compareDocumentPosition(b) & 16 : a !== b && a.contains(b) - }, hasScroll: function (a, b) { - if (c(a).css("overflow") === "hidden")return false; - b = b && b === "left" ? "scrollLeft" : "scrollTop"; - var d = false; - if (a[b] > 0)return true; - a[b] = 1; - d = a[b] > 0; - a[b] = 0; - return d - }, isOverAxis: function (a, b, d) { - return a > b && a < b + d - }, isOver: function (a, b, d, e, h, i) { - return c.ui.isOverAxis(a, d, h) && c.ui.isOverAxis(b, e, i) - }}) - } -})(jQuery); -; -/*! - * jQuery UI Widget 1.8.5 - * - * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Widget - */ -(function (b, j) { - if (b.cleanData) { - var k = b.cleanData; - b.cleanData = function (a) { - for (var c = 0, d; (d = a[c]) != null; c++)b(d).triggerHandler("remove"); - k(a) - } - } else { - var l = b.fn.remove; - b.fn.remove = function (a, c) { - return this.each(function () { - if (!c)if (!a || b.filter(a, [this]).length)b("*", this).add([this]).each(function () { - b(this).triggerHandler("remove") - }); - return l.call(b(this), a, c) - }) - } - } - b.widget = function (a, c, d) { - var e = a.split(".")[0], f; - a = a.split(".")[1]; - f = e + "-" + a; - if (!d) { - d = c; - c = b.Widget - } - b.expr[":"][f] = function (h) { - return!!b.data(h, - a) - }; - b[e] = b[e] || {}; - b[e][a] = function (h, g) { - arguments.length && this._createWidget(h, g) - }; - c = new c; - c.options = b.extend(true, {}, c.options); - b[e][a].prototype = b.extend(true, c, {namespace: e, widgetName: a, widgetEventPrefix: b[e][a].prototype.widgetEventPrefix || a, widgetBaseClass: f}, d); - b.widget.bridge(a, b[e][a]) - }; - b.widget.bridge = function (a, c) { - b.fn[a] = function (d) { - var e = typeof d === "string", f = Array.prototype.slice.call(arguments, 1), h = this; - d = !e && f.length ? b.extend.apply(null, [true, d].concat(f)) : d; - if (e && d.substring(0, 1) === - "_")return h; - e ? this.each(function () { - var g = b.data(this, a); - if (!g)throw"cannot call methods on " + a + " prior to initialization; attempted to call method '" + d + "'"; - if (!b.isFunction(g[d]))throw"no such method '" + d + "' for " + a + " widget instance"; - var i = g[d].apply(g, f); - if (i !== g && i !== j) { - h = i; - return false - } - }) : this.each(function () { - var g = b.data(this, a); - g ? g.option(d || {})._init() : b.data(this, a, new c(d, this)) - }); - return h - } - }; - b.Widget = function (a, c) { - arguments.length && this._createWidget(a, c) - }; - b.Widget.prototype = {widgetName: "widget", - widgetEventPrefix: "", options: {disabled: false}, _createWidget: function (a, c) { - b.data(c, this.widgetName, this); - this.element = b(c); - this.options = b.extend(true, {}, this.options, b.metadata && b.metadata.get(c)[this.widgetName], a); - var d = this; - this.element.bind("remove." + this.widgetName, function () { - d.destroy() - }); - this._create(); - this._init() - }, _create: function () { - }, _init: function () { - }, destroy: function () { - this.element.unbind("." + this.widgetName).removeData(this.widgetName); - this.widget().unbind("." + this.widgetName).removeAttr("aria-disabled").removeClass(this.widgetBaseClass + - "-disabled ui-state-disabled") - }, widget: function () { - return this.element - }, option: function (a, c) { - var d = a, e = this; - if (arguments.length === 0)return b.extend({}, e.options); - if (typeof a === "string") { - if (c === j)return this.options[a]; - d = {}; - d[a] = c - } - b.each(d, function (f, h) { - e._setOption(f, h) - }); - return e - }, _setOption: function (a, c) { - this.options[a] = c; - if (a === "disabled")this.widget()[c ? "addClass" : "removeClass"](this.widgetBaseClass + "-disabled ui-state-disabled").attr("aria-disabled", c); - return this - }, enable: function () { - return this._setOption("disabled", - false) - }, disable: function () { - return this._setOption("disabled", true) - }, _trigger: function (a, c, d) { - var e = this.options[a]; - c = b.Event(c); - c.type = (a === this.widgetEventPrefix ? a : this.widgetEventPrefix + a).toLowerCase(); - d = d || {}; - if (c.originalEvent) { - a = b.event.props.length; - for (var f; a;) { - f = b.event.props[--a]; - c[f] = c.originalEvent[f] - } - } - this.element.trigger(c, d); - return!(b.isFunction(e) && e.call(this.element[0], c, d) === false || c.isDefaultPrevented()) - }} -})(jQuery); -; -/*! - * jQuery UI Mouse 1.8.5 - * - * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Mouse - * - * Depends: - * jquery.ui.widget.js - */ -(function (c) { - c.widget("ui.mouse", {options: {cancel: ":input,option", distance: 1, delay: 0}, _mouseInit: function () { - var a = this; - this.element.bind("mousedown." + this.widgetName,function (b) { - return a._mouseDown(b) - }).bind("click." + this.widgetName, function (b) { - if (a._preventClickEvent) { - a._preventClickEvent = false; - b.stopImmediatePropagation(); - return false - } - }); - this.started = false - }, _mouseDestroy: function () { - this.element.unbind("." + this.widgetName) - }, _mouseDown: function (a) { - a.originalEvent = a.originalEvent || {}; - if (!a.originalEvent.mouseHandled) { - this._mouseStarted && - this._mouseUp(a); - this._mouseDownEvent = a; - var b = this, e = a.which == 1, f = typeof this.options.cancel == "string" ? c(a.target).parents().add(a.target).filter(this.options.cancel).length : false; - if (!e || f || !this._mouseCapture(a))return true; - this.mouseDelayMet = !this.options.delay; - if (!this.mouseDelayMet)this._mouseDelayTimer = setTimeout(function () { - b.mouseDelayMet = true - }, this.options.delay); - if (this._mouseDistanceMet(a) && this._mouseDelayMet(a)) { - this._mouseStarted = this._mouseStart(a) !== false; - if (!this._mouseStarted) { - a.preventDefault(); - return true - } - } - this._mouseMoveDelegate = function (d) { - return b._mouseMove(d) - }; - this._mouseUpDelegate = function (d) { - return b._mouseUp(d) - }; - c(document).bind("mousemove." + this.widgetName, this._mouseMoveDelegate).bind("mouseup." + this.widgetName, this._mouseUpDelegate); - c.browser.safari || a.preventDefault(); - return a.originalEvent.mouseHandled = true - } - }, _mouseMove: function (a) { - if (c.browser.msie && !a.button)return this._mouseUp(a); - if (this._mouseStarted) { - this._mouseDrag(a); - return a.preventDefault() - } - if (this._mouseDistanceMet(a) && - this._mouseDelayMet(a))(this._mouseStarted = this._mouseStart(this._mouseDownEvent, a) !== false) ? this._mouseDrag(a) : this._mouseUp(a); - return!this._mouseStarted - }, _mouseUp: function (a) { - c(document).unbind("mousemove." + this.widgetName, this._mouseMoveDelegate).unbind("mouseup." + this.widgetName, this._mouseUpDelegate); - if (this._mouseStarted) { - this._mouseStarted = false; - this._preventClickEvent = a.target == this._mouseDownEvent.target; - this._mouseStop(a) - } - return false - }, _mouseDistanceMet: function (a) { - return Math.max(Math.abs(this._mouseDownEvent.pageX - - a.pageX), Math.abs(this._mouseDownEvent.pageY - a.pageY)) >= this.options.distance - }, _mouseDelayMet: function () { - return this.mouseDelayMet - }, _mouseStart: function () { - }, _mouseDrag: function () { - }, _mouseStop: function () { - }, _mouseCapture: function () { - return true - }}) -})(jQuery); -; -/* - * jQuery UI Position 1.8.5 - * - * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Position - */ -(function (c) { - c.ui = c.ui || {}; - var n = /left|center|right/, o = /top|center|bottom/, t = c.fn.position, u = c.fn.offset; - c.fn.position = function (b) { - if (!b || !b.of)return t.apply(this, arguments); - b = c.extend({}, b); - var a = c(b.of), d = a[0], g = (b.collision || "flip").split(" "), e = b.offset ? b.offset.split(" ") : [0, 0], h, k, j; - if (d.nodeType === 9) { - h = a.width(); - k = a.height(); - j = {top: 0, left: 0} - } else if (d.scrollTo && d.document) { - h = a.width(); - k = a.height(); - j = {top: a.scrollTop(), left: a.scrollLeft()} - } else if (d.preventDefault) { - b.at = "left top"; - h = k = 0; - j = - {top: b.of.pageY, left: b.of.pageX} - } else { - h = a.outerWidth(); - k = a.outerHeight(); - j = a.offset() - } - c.each(["my", "at"], function () { - var f = (b[this] || "").split(" "); - if (f.length === 1)f = n.test(f[0]) ? f.concat(["center"]) : o.test(f[0]) ? ["center"].concat(f) : ["center", "center"]; - f[0] = n.test(f[0]) ? f[0] : "center"; - f[1] = o.test(f[1]) ? f[1] : "center"; - b[this] = f - }); - if (g.length === 1)g[1] = g[0]; - e[0] = parseInt(e[0], 10) || 0; - if (e.length === 1)e[1] = e[0]; - e[1] = parseInt(e[1], 10) || 0; - if (b.at[0] === "right")j.left += h; else if (b.at[0] === "center")j.left += h / - 2; - if (b.at[1] === "bottom")j.top += k; else if (b.at[1] === "center")j.top += k / 2; - j.left += e[0]; - j.top += e[1]; - return this.each(function () { - var f = c(this), l = f.outerWidth(), m = f.outerHeight(), p = parseInt(c.curCSS(this, "marginLeft", true)) || 0, q = parseInt(c.curCSS(this, "marginTop", true)) || 0, v = l + p + parseInt(c.curCSS(this, "marginRight", true)) || 0, w = m + q + parseInt(c.curCSS(this, "marginBottom", true)) || 0, i = c.extend({}, j), r; - if (b.my[0] === "right")i.left -= l; else if (b.my[0] === "center")i.left -= l / 2; - if (b.my[1] === "bottom")i.top -= m; else if (b.my[1] === - "center")i.top -= m / 2; - i.left = parseInt(i.left); - i.top = parseInt(i.top); - r = {left: i.left - p, top: i.top - q}; - c.each(["left", "top"], function (s, x) { - c.ui.position[g[s]] && c.ui.position[g[s]][x](i, {targetWidth: h, targetHeight: k, elemWidth: l, elemHeight: m, collisionPosition: r, collisionWidth: v, collisionHeight: w, offset: e, my: b.my, at: b.at}) - }); - c.fn.bgiframe && f.bgiframe(); - f.offset(c.extend(i, {using: b.using})) - }) - }; - c.ui.position = {fit: {left: function (b, a) { - var d = c(window); - d = a.collisionPosition.left + a.collisionWidth - d.width() - d.scrollLeft(); - b.left = d > 0 ? b.left - d : Math.max(b.left - a.collisionPosition.left, b.left) - }, top: function (b, a) { - var d = c(window); - d = a.collisionPosition.top + a.collisionHeight - d.height() - d.scrollTop(); - b.top = d > 0 ? b.top - d : Math.max(b.top - a.collisionPosition.top, b.top) - }}, flip: {left: function (b, a) { - if (a.at[0] !== "center") { - var d = c(window); - d = a.collisionPosition.left + a.collisionWidth - d.width() - d.scrollLeft(); - var g = a.my[0] === "left" ? -a.elemWidth : a.my[0] === "right" ? a.elemWidth : 0, e = a.at[0] === "left" ? a.targetWidth : -a.targetWidth, h = -2 * a.offset[0]; - b.left += a.collisionPosition.left < 0 ? g + e + h : d > 0 ? g + e + h : 0 - } - }, top: function (b, a) { - if (a.at[1] !== "center") { - var d = c(window); - d = a.collisionPosition.top + a.collisionHeight - d.height() - d.scrollTop(); - var g = a.my[1] === "top" ? -a.elemHeight : a.my[1] === "bottom" ? a.elemHeight : 0, e = a.at[1] === "top" ? a.targetHeight : -a.targetHeight, h = -2 * a.offset[1]; - b.top += a.collisionPosition.top < 0 ? g + e + h : d > 0 ? g + e + h : 0 - } - }}}; - if (!c.offset.setOffset) { - c.offset.setOffset = function (b, a) { - if (/static/.test(c.curCSS(b, "position")))b.style.position = "relative"; - var d = - c(b), g = d.offset(), e = parseInt(c.curCSS(b, "top", true), 10) || 0, h = parseInt(c.curCSS(b, "left", true), 10) || 0; - g = {top: a.top - g.top + e, left: a.left - g.left + h}; - "using"in a ? a.using.call(b, g) : d.css(g) - }; - c.fn.offset = function (b) { - var a = this[0]; - if (!a || !a.ownerDocument)return null; - if (b)return this.each(function () { - c.offset.setOffset(this, b) - }); - return u.call(this) - } - } -})(jQuery); -; -/* - * jQuery UI Slider 1.8.5 - * - * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Slider - * - * Depends: - * jquery.ui.core.js - * jquery.ui.mouse.js - * jquery.ui.widget.js - */ -(function (d) { - d.widget("ui.slider", d.ui.mouse, {widgetEventPrefix: "slide", options: {animate: false, distance: 0, max: 100, min: 0, orientation: "horizontal", range: false, step: 1, value: 0, values: null}, _create: function () { - var a = this, b = this.options; - this._mouseSliding = this._keySliding = false; - this._animateOff = true; - this._handleIndex = null; - this._detectOrientation(); - this._mouseInit(); - this.element.addClass("ui-slider ui-slider-" + this.orientation + " ui-widget ui-widget-content ui-corner-all"); - b.disabled && this.element.addClass("ui-slider-disabled ui-disabled"); - this.range = d([]); - if (b.range) { - if (b.range === true) { - this.range = d("
"); - if (!b.values)b.values = [this._valueMin(), this._valueMin()]; - if (b.values.length && b.values.length !== 2)b.values = [b.values[0], b.values[0]] - } else this.range = d("
"); - this.range.appendTo(this.element).addClass("ui-slider-range"); - if (b.range === "min" || b.range === "max")this.range.addClass("ui-slider-range-" + b.range); - this.range.addClass("ui-widget-header") - } - d(".ui-slider-handle", this.element).length === 0 && d("").appendTo(this.element).addClass("ui-slider-handle"); - if (b.values && b.values.length)for (; d(".ui-slider-handle", this.element).length < b.values.length;)d("").appendTo(this.element).addClass("ui-slider-handle"); - this.handles = d(".ui-slider-handle", this.element).addClass("ui-state-default ui-corner-all"); - this.handle = this.handles.eq(0); - this.handles.add(this.range).filter("a").click(function (c) { - c.preventDefault() - }).hover(function () { - b.disabled || d(this).addClass("ui-state-hover") - },function () { - d(this).removeClass("ui-state-hover") - }).focus(function () { - if (b.disabled)d(this).blur(); - else { - d(".ui-slider .ui-state-focus").removeClass("ui-state-focus"); - d(this).addClass("ui-state-focus") - } - }).blur(function () { - d(this).removeClass("ui-state-focus") - }); - this.handles.each(function (c) { - d(this).data("index.ui-slider-handle", c) - }); - this.handles.keydown(function (c) { - var e = true, f = d(this).data("index.ui-slider-handle"), h, g, i; - if (!a.options.disabled) { - switch (c.keyCode) { - case d.ui.keyCode.HOME: - case d.ui.keyCode.END: - case d.ui.keyCode.PAGE_UP: - case d.ui.keyCode.PAGE_DOWN: - case d.ui.keyCode.UP: - case d.ui.keyCode.RIGHT: - case d.ui.keyCode.DOWN: - case d.ui.keyCode.LEFT: - e = - false; - if (!a._keySliding) { - a._keySliding = true; - d(this).addClass("ui-state-active"); - h = a._start(c, f); - if (h === false)return - } - break - } - i = a.options.step; - h = a.options.values && a.options.values.length ? (g = a.values(f)) : (g = a.value()); - switch (c.keyCode) { - case d.ui.keyCode.HOME: - g = a._valueMin(); - break; - case d.ui.keyCode.END: - g = a._valueMax(); - break; - case d.ui.keyCode.PAGE_UP: - g = a._trimAlignValue(h + (a._valueMax() - a._valueMin()) / 5); - break; - case d.ui.keyCode.PAGE_DOWN: - g = a._trimAlignValue(h - (a._valueMax() - a._valueMin()) / 5); - break; - case d.ui.keyCode.UP: - case d.ui.keyCode.RIGHT: - if (h === - a._valueMax())return; - g = a._trimAlignValue(h + i); - break; - case d.ui.keyCode.DOWN: - case d.ui.keyCode.LEFT: - if (h === a._valueMin())return; - g = a._trimAlignValue(h - i); - break - } - a._slide(c, f, g); - return e - } - }).keyup(function (c) { - var e = d(this).data("index.ui-slider-handle"); - if (a._keySliding) { - a._keySliding = false; - a._stop(c, e); - a._change(c, e); - d(this).removeClass("ui-state-active") - } - }); - this._refreshValue(); - this._animateOff = false - }, destroy: function () { - this.handles.remove(); - this.range.remove(); - this.element.removeClass("ui-slider ui-slider-horizontal ui-slider-vertical ui-slider-disabled ui-widget ui-widget-content ui-corner-all").removeData("slider").unbind(".slider"); - this._mouseDestroy(); - return this - }, _mouseCapture: function (a) { - var b = this.options, c, e, f, h, g; - if (b.disabled)return false; - this.elementSize = {width: this.element.outerWidth(), height: this.element.outerHeight()}; - this.elementOffset = this.element.offset(); - c = this._normValueFromMouse({x: a.pageX, y: a.pageY}); - e = this._valueMax() - this._valueMin() + 1; - h = this; - this.handles.each(function (i) { - var j = Math.abs(c - h.values(i)); - if (e > j) { - e = j; - f = d(this); - g = i - } - }); - if (b.range === true && this.values(1) === b.min) { - g += 1; - f = d(this.handles[g]) - } - if (this._start(a, - g) === false)return false; - this._mouseSliding = true; - h._handleIndex = g; - f.addClass("ui-state-active").focus(); - b = f.offset(); - this._clickOffset = !d(a.target).parents().andSelf().is(".ui-slider-handle") ? {left: 0, top: 0} : {left: a.pageX - b.left - f.width() / 2, top: a.pageY - b.top - f.height() / 2 - (parseInt(f.css("borderTopWidth"), 10) || 0) - (parseInt(f.css("borderBottomWidth"), 10) || 0) + (parseInt(f.css("marginTop"), 10) || 0)}; - this._slide(a, g, c); - return this._animateOff = true - }, _mouseStart: function () { - return true - }, _mouseDrag: function (a) { - var b = - this._normValueFromMouse({x: a.pageX, y: a.pageY}); - this._slide(a, this._handleIndex, b); - return false - }, _mouseStop: function (a) { - this.handles.removeClass("ui-state-active"); - this._mouseSliding = false; - this._stop(a, this._handleIndex); - this._change(a, this._handleIndex); - this._clickOffset = this._handleIndex = null; - return this._animateOff = false - }, _detectOrientation: function () { - this.orientation = this.options.orientation === "vertical" ? "vertical" : "horizontal" - }, _normValueFromMouse: function (a) { - var b; - if (this.orientation === "horizontal") { - b = - this.elementSize.width; - a = a.x - this.elementOffset.left - (this._clickOffset ? this._clickOffset.left : 0) - } else { - b = this.elementSize.height; - a = a.y - this.elementOffset.top - (this._clickOffset ? this._clickOffset.top : 0) - } - b = a / b; - if (b > 1)b = 1; - if (b < 0)b = 0; - if (this.orientation === "vertical")b = 1 - b; - a = this._valueMax() - this._valueMin(); - return this._trimAlignValue(this._valueMin() + b * a) - }, _start: function (a, b) { - var c = {handle: this.handles[b], value: this.value()}; - if (this.options.values && this.options.values.length) { - c.value = this.values(b); - c.values = this.values() - } - return this._trigger("start", a, c) - }, _slide: function (a, b, c) { - var e; - if (this.options.values && this.options.values.length) { - e = this.values(b ? 0 : 1); - if (this.options.values.length === 2 && this.options.range === true && (b === 0 && c > e || b === 1 && c < e))c = e; - if (c !== this.values(b)) { - e = this.values(); - e[b] = c; - a = this._trigger("slide", a, {handle: this.handles[b], value: c, values: e}); - this.values(b ? 0 : 1); - a !== false && this.values(b, c, true) - } - } else if (c !== this.value()) { - a = this._trigger("slide", a, {handle: this.handles[b], value: c}); - a !== false && this.value(c) - } - }, _stop: function (a, b) { - var c = {handle: this.handles[b], value: this.value()}; - if (this.options.values && this.options.values.length) { - c.value = this.values(b); - c.values = this.values() - } - this._trigger("stop", a, c) - }, _change: function (a, b) { - if (!this._keySliding && !this._mouseSliding) { - var c = {handle: this.handles[b], value: this.value()}; - if (this.options.values && this.options.values.length) { - c.value = this.values(b); - c.values = this.values() - } - this._trigger("change", a, c) - } - }, value: function (a) { - if (arguments.length) { - this.options.value = - this._trimAlignValue(a); - this._refreshValue(); - this._change(null, 0) - } - return this._value() - }, values: function (a, b) { - var c, e, f; - if (arguments.length > 1) { - this.options.values[a] = this._trimAlignValue(b); - this._refreshValue(); - this._change(null, a) - } - if (arguments.length)if (d.isArray(arguments[0])) { - c = this.options.values; - e = arguments[0]; - for (f = 0; f < c.length; f += 1) { - c[f] = this._trimAlignValue(e[f]); - this._change(null, f) - } - this._refreshValue() - } else return this.options.values && this.options.values.length ? this._values(a) : this.value(); - else return this._values() - }, _setOption: function (a, b) { - var c, e = 0; - if (d.isArray(this.options.values))e = this.options.values.length; - d.Widget.prototype._setOption.apply(this, arguments); - switch (a) { - case "disabled": - if (b) { - this.handles.filter(".ui-state-focus").blur(); - this.handles.removeClass("ui-state-hover"); - this.handles.attr("disabled", "disabled"); - this.element.addClass("ui-disabled") - } else { - this.handles.removeAttr("disabled"); - this.element.removeClass("ui-disabled") - } - break; - case "orientation": - this._detectOrientation(); - this.element.removeClass("ui-slider-horizontal ui-slider-vertical").addClass("ui-slider-" + this.orientation); - this._refreshValue(); - break; - case "value": - this._animateOff = true; - this._refreshValue(); - this._change(null, 0); - this._animateOff = false; - break; - case "values": - this._animateOff = true; - this._refreshValue(); - for (c = 0; c < e; c += 1)this._change(null, c); - this._animateOff = false; - break - } - }, _value: function () { - var a = this.options.value; - return a = this._trimAlignValue(a) - }, _values: function (a) { - var b, c; - if (arguments.length) { - b = this.options.values[a]; - return b = this._trimAlignValue(b) - } else { - b = this.options.values.slice(); - for (c = 0; c < b.length; c += 1)b[c] = this._trimAlignValue(b[c]); - return b - } - }, _trimAlignValue: function (a) { - if (a < this._valueMin())return this._valueMin(); - if (a > this._valueMax())return this._valueMax(); - var b = this.options.step > 0 ? this.options.step : 1, c = a % b; - a = a - c; - if (Math.abs(c) * 2 >= b)a += c > 0 ? b : -b; - return parseFloat(a.toFixed(5)) - }, _valueMin: function () { - return this.options.min - }, _valueMax: function () { - return this.options.max - }, _refreshValue: function () { - var a = - this.options.range, b = this.options, c = this, e = !this._animateOff ? b.animate : false, f, h = {}, g, i, j, l; - if (this.options.values && this.options.values.length)this.handles.each(function (k) { - f = (c.values(k) - c._valueMin()) / (c._valueMax() - c._valueMin()) * 100; - h[c.orientation === "horizontal" ? "left" : "bottom"] = f + "%"; - d(this).stop(1, 1)[e ? "animate" : "css"](h, b.animate); - if (c.options.range === true)if (c.orientation === "horizontal") { - if (k === 0)c.range.stop(1, 1)[e ? "animate" : "css"]({left: f + "%"}, b.animate); - if (k === 1)c.range[e ? "animate" : "css"]({width: f - - g + "%"}, {queue: false, duration: b.animate}) - } else { - if (k === 0)c.range.stop(1, 1)[e ? "animate" : "css"]({bottom: f + "%"}, b.animate); - if (k === 1)c.range[e ? "animate" : "css"]({height: f - g + "%"}, {queue: false, duration: b.animate}) - } - g = f - }); else { - i = this.value(); - j = this._valueMin(); - l = this._valueMax(); - f = l !== j ? (i - j) / (l - j) * 100 : 0; - h[c.orientation === "horizontal" ? "left" : "bottom"] = f + "%"; - this.handle.stop(1, 1)[e ? "animate" : "css"](h, b.animate); - if (a === "min" && this.orientation === "horizontal")this.range.stop(1, 1)[e ? "animate" : "css"]({width: f + "%"}, - b.animate); - if (a === "max" && this.orientation === "horizontal")this.range[e ? "animate" : "css"]({width: 100 - f + "%"}, {queue: false, duration: b.animate}); - if (a === "min" && this.orientation === "vertical")this.range.stop(1, 1)[e ? "animate" : "css"]({height: f + "%"}, b.animate); - if (a === "max" && this.orientation === "vertical")this.range[e ? "animate" : "css"]({height: 100 - f + "%"}, {queue: false, duration: b.animate}) - } - }}); - d.extend(d.ui.slider, {version: "1.8.5"}) -})(jQuery); -; -/* - * jQuery UI Datepicker 1.8.5 - * - * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Datepicker - * - * Depends: - * jquery.ui.core.js - */ -(function (d, G) { - function L() { - this.debug = false; - this._curInst = null; - this._keyEvent = false; - this._disabledInputs = []; - this._inDialog = this._datepickerShowing = false; - this._mainDivId = "ui-datepicker-div"; - this._inlineClass = "ui-datepicker-inline"; - this._appendClass = "ui-datepicker-append"; - this._triggerClass = "ui-datepicker-trigger"; - this._dialogClass = "ui-datepicker-dialog"; - this._disableClass = "ui-datepicker-disabled"; - this._unselectableClass = "ui-datepicker-unselectable"; - this._currentClass = "ui-datepicker-current-day"; - this._dayOverClass = - "ui-datepicker-days-cell-over"; - this.regional = []; - this.regional[""] = {closeText: "Done", prevText: "Prev", nextText: "Next", currentText: "Today", monthNames: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"], monthNamesShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"], dayNames: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"], dayNamesShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"], dayNamesMin: ["Su", - "Mo", "Tu", "We", "Th", "Fr", "Sa"], weekHeader: "Wk", dateFormat: "mm/dd/yy", firstDay: 0, isRTL: false, showMonthAfterYear: false, yearSuffix: ""}; - this._defaults = {showOn: "focus", showAnim: "fadeIn", showOptions: {}, defaultDate: null, appendText: "", buttonText: "...", buttonImage: "", buttonImageOnly: false, hideIfNoPrevNext: false, navigationAsDateFormat: false, gotoCurrent: false, changeMonth: false, changeYear: false, yearRange: "c-10:c+10", showOtherMonths: false, selectOtherMonths: false, showWeek: false, calculateWeek: this.iso8601Week, shortYearCutoff: "+10", - minDate: null, maxDate: null, duration: "fast", beforeShowDay: null, beforeShow: null, onSelect: null, onChangeMonthYear: null, onClose: null, numberOfMonths: 1, showCurrentAtPos: 0, stepMonths: 1, stepBigMonths: 12, altField: "", altFormat: "", constrainInput: true, showButtonPanel: false, autoSize: false}; - d.extend(this._defaults, this.regional[""]); - this.dpDiv = d('
') - } - - function E(a, b) { - d.extend(a, - b); - for (var c in b)if (b[c] == null || b[c] == G)a[c] = b[c]; - return a - } - - d.extend(d.ui, {datepicker: {version: "1.8.5"}}); - var y = (new Date).getTime(); - d.extend(L.prototype, {markerClassName: "hasDatepicker", log: function () { - this.debug && console.log.apply("", arguments) - }, _widgetDatepicker: function () { - return this.dpDiv - }, setDefaults: function (a) { - E(this._defaults, a || {}); - return this - }, _attachDatepicker: function (a, b) { - var c = null; - for (var e in this._defaults) { - var f = a.getAttribute("date:" + e); - if (f) { - c = c || {}; - try { - c[e] = eval(f) - } catch (h) { - c[e] = - f - } - } - } - e = a.nodeName.toLowerCase(); - f = e == "div" || e == "span"; - if (!a.id) { - this.uuid += 1; - a.id = "dp" + this.uuid - } - var i = this._newInst(d(a), f); - i.settings = d.extend({}, b || {}, c || {}); - if (e == "input")this._connectDatepicker(a, i); else f && this._inlineDatepicker(a, i) - }, _newInst: function (a, b) { - return{id: a[0].id.replace(/([^A-Za-z0-9_])/g, "\\\\$1"), input: a, selectedDay: 0, selectedMonth: 0, selectedYear: 0, drawMonth: 0, drawYear: 0, inline: b, dpDiv: !b ? this.dpDiv : d('
')} - }, - _connectDatepicker: function (a, b) { - var c = d(a); - b.append = d([]); - b.trigger = d([]); - if (!c.hasClass(this.markerClassName)) { - this._attachments(c, b); - c.addClass(this.markerClassName).keydown(this._doKeyDown).keypress(this._doKeyPress).keyup(this._doKeyUp).bind("setData.datepicker",function (e, f, h) { - b.settings[f] = h - }).bind("getData.datepicker", function (e, f) { - return this._get(b, f) - }); - this._autoSize(b); - d.data(a, "datepicker", b) - } - }, _attachments: function (a, b) { - var c = this._get(b, "appendText"), e = this._get(b, "isRTL"); - b.append && - b.append.remove(); - if (c) { - b.append = d('' + c + ""); - a[e ? "before" : "after"](b.append) - } - a.unbind("focus", this._showDatepicker); - b.trigger && b.trigger.remove(); - c = this._get(b, "showOn"); - if (c == "focus" || c == "both")a.focus(this._showDatepicker); - if (c == "button" || c == "both") { - c = this._get(b, "buttonText"); - var f = this._get(b, "buttonImage"); - b.trigger = d(this._get(b, "buttonImageOnly") ? d("").addClass(this._triggerClass).attr({src: f, alt: c, title: c}) : d('').addClass(this._triggerClass).html(f == - "" ? c : d("").attr({src: f, alt: c, title: c}))); - a[e ? "before" : "after"](b.trigger); - b.trigger.click(function () { - d.datepicker._datepickerShowing && d.datepicker._lastInput == a[0] ? d.datepicker._hideDatepicker() : d.datepicker._showDatepicker(a[0]); - return false - }) - } - }, _autoSize: function (a) { - if (this._get(a, "autoSize") && !a.inline) { - var b = new Date(2009, 11, 20), c = this._get(a, "dateFormat"); - if (c.match(/[DM]/)) { - var e = function (f) { - for (var h = 0, i = 0, g = 0; g < f.length; g++)if (f[g].length > h) { - h = f[g].length; - i = g - } - return i - }; - b.setMonth(e(this._get(a, - c.match(/MM/) ? "monthNames" : "monthNamesShort"))); - b.setDate(e(this._get(a, c.match(/DD/) ? "dayNames" : "dayNamesShort")) + 20 - b.getDay()) - } - a.input.attr("size", this._formatDate(a, b).length) - } - }, _inlineDatepicker: function (a, b) { - var c = d(a); - if (!c.hasClass(this.markerClassName)) { - c.addClass(this.markerClassName).append(b.dpDiv).bind("setData.datepicker",function (e, f, h) { - b.settings[f] = h - }).bind("getData.datepicker", function (e, f) { - return this._get(b, f) - }); - d.data(a, "datepicker", b); - this._setDate(b, this._getDefaultDate(b), - true); - this._updateDatepicker(b); - this._updateAlternate(b) - } - }, _dialogDatepicker: function (a, b, c, e, f) { - a = this._dialogInst; - if (!a) { - this.uuid += 1; - this._dialogInput = d(''); - this._dialogInput.keydown(this._doKeyDown); - d("body").append(this._dialogInput); - a = this._dialogInst = this._newInst(this._dialogInput, false); - a.settings = {}; - d.data(this._dialogInput[0], "datepicker", a) - } - E(a.settings, e || {}); - b = b && b.constructor == - Date ? this._formatDate(a, b) : b; - this._dialogInput.val(b); - this._pos = f ? f.length ? f : [f.pageX, f.pageY] : null; - if (!this._pos)this._pos = [document.documentElement.clientWidth / 2 - 100 + (document.documentElement.scrollLeft || document.body.scrollLeft), document.documentElement.clientHeight / 2 - 150 + (document.documentElement.scrollTop || document.body.scrollTop)]; - this._dialogInput.css("left", this._pos[0] + 20 + "px").css("top", this._pos[1] + "px"); - a.settings.onSelect = c; - this._inDialog = true; - this.dpDiv.addClass(this._dialogClass); - this._showDatepicker(this._dialogInput[0]); - d.blockUI && d.blockUI(this.dpDiv); - d.data(this._dialogInput[0], "datepicker", a); - return this - }, _destroyDatepicker: function (a) { - var b = d(a), c = d.data(a, "datepicker"); - if (b.hasClass(this.markerClassName)) { - var e = a.nodeName.toLowerCase(); - d.removeData(a, "datepicker"); - if (e == "input") { - c.append.remove(); - c.trigger.remove(); - b.removeClass(this.markerClassName).unbind("focus", this._showDatepicker).unbind("keydown", this._doKeyDown).unbind("keypress", this._doKeyPress).unbind("keyup", this._doKeyUp) - } else if (e == "div" || e == "span")b.removeClass(this.markerClassName).empty() - } - }, - _enableDatepicker: function (a) { - var b = d(a), c = d.data(a, "datepicker"); - if (b.hasClass(this.markerClassName)) { - var e = a.nodeName.toLowerCase(); - if (e == "input") { - a.disabled = false; - c.trigger.filter("button").each(function () { - this.disabled = false - }).end().filter("img").css({opacity: "1.0", cursor: ""}) - } else if (e == "div" || e == "span")b.children("." + this._inlineClass).children().removeClass("ui-state-disabled"); - this._disabledInputs = d.map(this._disabledInputs, function (f) { - return f == a ? null : f - }) - } - }, _disableDatepicker: function (a) { - var b = - d(a), c = d.data(a, "datepicker"); - if (b.hasClass(this.markerClassName)) { - var e = a.nodeName.toLowerCase(); - if (e == "input") { - a.disabled = true; - c.trigger.filter("button").each(function () { - this.disabled = true - }).end().filter("img").css({opacity: "0.5", cursor: "default"}) - } else if (e == "div" || e == "span")b.children("." + this._inlineClass).children().addClass("ui-state-disabled"); - this._disabledInputs = d.map(this._disabledInputs, function (f) { - return f == a ? null : f - }); - this._disabledInputs[this._disabledInputs.length] = a - } - }, _isDisabledDatepicker: function (a) { - if (!a)return false; - for (var b = 0; b < this._disabledInputs.length; b++)if (this._disabledInputs[b] == a)return true; - return false - }, _getInst: function (a) { - try { - return d.data(a, "datepicker") - } catch (b) { - throw"Missing instance data for this datepicker"; - } - }, _optionDatepicker: function (a, b, c) { - var e = this._getInst(a); - if (arguments.length == 2 && typeof b == "string")return b == "defaults" ? d.extend({}, d.datepicker._defaults) : e ? b == "all" ? d.extend({}, e.settings) : this._get(e, b) : null; - var f = b || {}; - if (typeof b == "string") { - f = {}; - f[b] = c - } - if (e) { - this._curInst == e && - this._hideDatepicker(); - var h = this._getDateDatepicker(a, true); - E(e.settings, f); - this._attachments(d(a), e); - this._autoSize(e); - this._setDateDatepicker(a, h); - this._updateDatepicker(e) - } - }, _changeDatepicker: function (a, b, c) { - this._optionDatepicker(a, b, c) - }, _refreshDatepicker: function (a) { - (a = this._getInst(a)) && this._updateDatepicker(a) - }, _setDateDatepicker: function (a, b) { - if (a = this._getInst(a)) { - this._setDate(a, b); - this._updateDatepicker(a); - this._updateAlternate(a) - } - }, _getDateDatepicker: function (a, b) { - (a = this._getInst(a)) && !a.inline && this._setDateFromField(a, b); - return a ? this._getDate(a) : null - }, _doKeyDown: function (a) { - var b = d.datepicker._getInst(a.target), c = true, e = b.dpDiv.is(".ui-datepicker-rtl"); - b._keyEvent = true; - if (d.datepicker._datepickerShowing)switch (a.keyCode) { - case 9: - d.datepicker._hideDatepicker(); - c = false; - break; - case 13: - c = d("td." + d.datepicker._dayOverClass, b.dpDiv).add(d("td." + d.datepicker._currentClass, b.dpDiv)); - c[0] ? d.datepicker._selectDay(a.target, b.selectedMonth, b.selectedYear, c[0]) : d.datepicker._hideDatepicker(); - return false; - case 27: - d.datepicker._hideDatepicker(); - break; - case 33: - d.datepicker._adjustDate(a.target, a.ctrlKey ? -d.datepicker._get(b, "stepBigMonths") : -d.datepicker._get(b, "stepMonths"), "M"); - break; - case 34: - d.datepicker._adjustDate(a.target, a.ctrlKey ? +d.datepicker._get(b, "stepBigMonths") : +d.datepicker._get(b, "stepMonths"), "M"); - break; - case 35: - if (a.ctrlKey || a.metaKey)d.datepicker._clearDate(a.target); - c = a.ctrlKey || a.metaKey; - break; - case 36: - if (a.ctrlKey || a.metaKey)d.datepicker._gotoToday(a.target); - c = a.ctrlKey || - a.metaKey; - break; - case 37: - if (a.ctrlKey || a.metaKey)d.datepicker._adjustDate(a.target, e ? +1 : -1, "D"); - c = a.ctrlKey || a.metaKey; - if (a.originalEvent.altKey)d.datepicker._adjustDate(a.target, a.ctrlKey ? -d.datepicker._get(b, "stepBigMonths") : -d.datepicker._get(b, "stepMonths"), "M"); - break; - case 38: - if (a.ctrlKey || a.metaKey)d.datepicker._adjustDate(a.target, -7, "D"); - c = a.ctrlKey || a.metaKey; - break; - case 39: - if (a.ctrlKey || a.metaKey)d.datepicker._adjustDate(a.target, e ? -1 : +1, "D"); - c = a.ctrlKey || a.metaKey; - if (a.originalEvent.altKey)d.datepicker._adjustDate(a.target, - a.ctrlKey ? +d.datepicker._get(b, "stepBigMonths") : +d.datepicker._get(b, "stepMonths"), "M"); - break; - case 40: - if (a.ctrlKey || a.metaKey)d.datepicker._adjustDate(a.target, +7, "D"); - c = a.ctrlKey || a.metaKey; - break; - default: - c = false - } else if (a.keyCode == 36 && a.ctrlKey)d.datepicker._showDatepicker(this); else c = false; - if (c) { - a.preventDefault(); - a.stopPropagation() - } - }, _doKeyPress: function (a) { - var b = d.datepicker._getInst(a.target); - if (d.datepicker._get(b, "constrainInput")) { - b = d.datepicker._possibleChars(d.datepicker._get(b, "dateFormat")); - var c = String.fromCharCode(a.charCode == G ? a.keyCode : a.charCode); - return a.ctrlKey || c < " " || !b || b.indexOf(c) > -1 - } - }, _doKeyUp: function (a) { - a = d.datepicker._getInst(a.target); - if (a.input.val() != a.lastVal)try { - if (d.datepicker.parseDate(d.datepicker._get(a, "dateFormat"), a.input ? a.input.val() : null, d.datepicker._getFormatConfig(a))) { - d.datepicker._setDateFromField(a); - d.datepicker._updateAlternate(a); - d.datepicker._updateDatepicker(a) - } - } catch (b) { - d.datepicker.log(b) - } - return true - }, _showDatepicker: function (a) { - a = a.target || - a; - if (a.nodeName.toLowerCase() != "input")a = d("input", a.parentNode)[0]; - if (!(d.datepicker._isDisabledDatepicker(a) || d.datepicker._lastInput == a)) { - var b = d.datepicker._getInst(a); - d.datepicker._curInst && d.datepicker._curInst != b && d.datepicker._curInst.dpDiv.stop(true, true); - var c = d.datepicker._get(b, "beforeShow"); - E(b.settings, c ? c.apply(a, [a, b]) : {}); - b.lastVal = null; - d.datepicker._lastInput = a; - d.datepicker._setDateFromField(b); - if (d.datepicker._inDialog)a.value = ""; - if (!d.datepicker._pos) { - d.datepicker._pos = d.datepicker._findPos(a); - d.datepicker._pos[1] += a.offsetHeight - } - var e = false; - d(a).parents().each(function () { - e |= d(this).css("position") == "fixed"; - return!e - }); - if (e && d.browser.opera) { - d.datepicker._pos[0] -= document.documentElement.scrollLeft; - d.datepicker._pos[1] -= document.documentElement.scrollTop - } - c = {left: d.datepicker._pos[0], top: d.datepicker._pos[1]}; - d.datepicker._pos = null; - b.dpDiv.css({position: "absolute", display: "block", top: "-1000px"}); - d.datepicker._updateDatepicker(b); - c = d.datepicker._checkOffset(b, c, e); - b.dpDiv.css({position: d.datepicker._inDialog && - d.blockUI ? "static" : e ? "fixed" : "absolute", display: "none", left: c.left + "px", top: c.top + "px"}); - if (!b.inline) { - c = d.datepicker._get(b, "showAnim"); - var f = d.datepicker._get(b, "duration"), h = function () { - d.datepicker._datepickerShowing = true; - var i = d.datepicker._getBorders(b.dpDiv); - b.dpDiv.find("iframe.ui-datepicker-cover").css({left: -i[0], top: -i[1], width: b.dpDiv.outerWidth(), height: b.dpDiv.outerHeight()}) - }; - b.dpDiv.zIndex(d(a).zIndex() + 1); - d.effects && d.effects[c] ? b.dpDiv.show(c, d.datepicker._get(b, "showOptions"), f, - h) : b.dpDiv[c || "show"](c ? f : null, h); - if (!c || !f)h(); - b.input.is(":visible") && !b.input.is(":disabled") && b.input.focus(); - d.datepicker._curInst = b - } - } - }, _updateDatepicker: function (a) { - var b = this, c = d.datepicker._getBorders(a.dpDiv); - a.dpDiv.empty().append(this._generateHTML(a)).find("iframe.ui-datepicker-cover").css({left: -c[0], top: -c[1], width: a.dpDiv.outerWidth(), height: a.dpDiv.outerHeight()}).end().find("button, .ui-datepicker-prev, .ui-datepicker-next, .ui-datepicker-calendar td a").bind("mouseout",function () { - d(this).removeClass("ui-state-hover"); - this.className.indexOf("ui-datepicker-prev") != -1 && d(this).removeClass("ui-datepicker-prev-hover"); - this.className.indexOf("ui-datepicker-next") != -1 && d(this).removeClass("ui-datepicker-next-hover") - }).bind("mouseover",function () { - if (!b._isDisabledDatepicker(a.inline ? a.dpDiv.parent()[0] : a.input[0])) { - d(this).parents(".ui-datepicker-calendar").find("a").removeClass("ui-state-hover"); - d(this).addClass("ui-state-hover"); - this.className.indexOf("ui-datepicker-prev") != -1 && d(this).addClass("ui-datepicker-prev-hover"); - this.className.indexOf("ui-datepicker-next") != -1 && d(this).addClass("ui-datepicker-next-hover") - } - }).end().find("." + this._dayOverClass + " a").trigger("mouseover").end(); - c = this._getNumberOfMonths(a); - var e = c[1]; - e > 1 ? a.dpDiv.addClass("ui-datepicker-multi-" + e).css("width", 17 * e + "em") : a.dpDiv.removeClass("ui-datepicker-multi-2 ui-datepicker-multi-3 ui-datepicker-multi-4").width(""); - a.dpDiv[(c[0] != 1 || c[1] != 1 ? "add" : "remove") + "Class"]("ui-datepicker-multi"); - a.dpDiv[(this._get(a, "isRTL") ? "add" : "remove") + "Class"]("ui-datepicker-rtl"); - a == d.datepicker._curInst && d.datepicker._datepickerShowing && a.input && a.input.is(":visible") && !a.input.is(":disabled") && a.input.focus() - }, _getBorders: function (a) { - var b = function (c) { - return{thin: 1, medium: 2, thick: 3}[c] || c - }; - return[parseFloat(b(a.css("border-left-width"))), parseFloat(b(a.css("border-top-width")))] - }, _checkOffset: function (a, b, c) { - var e = a.dpDiv.outerWidth(), f = a.dpDiv.outerHeight(), h = a.input ? a.input.outerWidth() : 0, i = a.input ? a.input.outerHeight() : 0, g = document.documentElement.clientWidth + d(document).scrollLeft(), - k = document.documentElement.clientHeight + d(document).scrollTop(); - b.left -= this._get(a, "isRTL") ? e - h : 0; - b.left -= c && b.left == a.input.offset().left ? d(document).scrollLeft() : 0; - b.top -= c && b.top == a.input.offset().top + i ? d(document).scrollTop() : 0; - b.left -= Math.min(b.left, b.left + e > g && g > e ? Math.abs(b.left + e - g) : 0); - b.top -= Math.min(b.top, b.top + f > k && k > f ? Math.abs(f + i) : 0); - return b - }, _findPos: function (a) { - for (var b = this._get(this._getInst(a), "isRTL"); a && (a.type == "hidden" || a.nodeType != 1);)a = a[b ? "previousSibling" : "nextSibling"]; - a = d(a).offset(); - return[a.left, a.top] - }, _hideDatepicker: function (a) { - var b = this._curInst; - if (!(!b || a && b != d.data(a, "datepicker")))if (this._datepickerShowing) { - a = this._get(b, "showAnim"); - var c = this._get(b, "duration"), e = function () { - d.datepicker._tidyDialog(b); - this._curInst = null - }; - d.effects && d.effects[a] ? b.dpDiv.hide(a, d.datepicker._get(b, "showOptions"), c, e) : b.dpDiv[a == "slideDown" ? "slideUp" : a == "fadeIn" ? "fadeOut" : "hide"](a ? c : null, e); - a || e(); - if (a = this._get(b, "onClose"))a.apply(b.input ? b.input[0] : null, [b.input ? b.input.val() : - "", b]); - this._datepickerShowing = false; - this._lastInput = null; - if (this._inDialog) { - this._dialogInput.css({position: "absolute", left: "0", top: "-100px"}); - if (d.blockUI) { - d.unblockUI(); - d("body").append(this.dpDiv) - } - } - this._inDialog = false - } - }, _tidyDialog: function (a) { - a.dpDiv.removeClass(this._dialogClass).unbind(".ui-datepicker-calendar") - }, _checkExternalClick: function (a) { - if (d.datepicker._curInst) { - a = d(a.target); - a[0].id != d.datepicker._mainDivId && a.parents("#" + d.datepicker._mainDivId).length == 0 && !a.hasClass(d.datepicker.markerClassName) && !a.hasClass(d.datepicker._triggerClass) && d.datepicker._datepickerShowing && !(d.datepicker._inDialog && d.blockUI) && d.datepicker._hideDatepicker() - } - }, _adjustDate: function (a, b, c) { - a = d(a); - var e = this._getInst(a[0]); - if (!this._isDisabledDatepicker(a[0])) { - this._adjustInstDate(e, b + (c == "M" ? this._get(e, "showCurrentAtPos") : 0), c); - this._updateDatepicker(e) - } - }, _gotoToday: function (a) { - a = d(a); - var b = this._getInst(a[0]); - if (this._get(b, "gotoCurrent") && b.currentDay) { - b.selectedDay = b.currentDay; - b.drawMonth = b.selectedMonth = b.currentMonth; - b.drawYear = b.selectedYear = b.currentYear - } else { - var c = new Date; - b.selectedDay = c.getDate(); - b.drawMonth = b.selectedMonth = c.getMonth(); - b.drawYear = b.selectedYear = c.getFullYear() - } - this._notifyChange(b); - this._adjustDate(a) - }, _selectMonthYear: function (a, b, c) { - a = d(a); - var e = this._getInst(a[0]); - e._selectingMonthYear = false; - e["selected" + (c == "M" ? "Month" : "Year")] = e["draw" + (c == "M" ? "Month" : "Year")] = parseInt(b.options[b.selectedIndex].value, 10); - this._notifyChange(e); - this._adjustDate(a) - }, _clickMonthYear: function (a) { - var b = - this._getInst(d(a)[0]); - b.input && b._selectingMonthYear && setTimeout(function () { - b.input.focus() - }, 0); - b._selectingMonthYear = !b._selectingMonthYear - }, _selectDay: function (a, b, c, e) { - var f = d(a); - if (!(d(e).hasClass(this._unselectableClass) || this._isDisabledDatepicker(f[0]))) { - f = this._getInst(f[0]); - f.selectedDay = f.currentDay = d("a", e).html(); - f.selectedMonth = f.currentMonth = b; - f.selectedYear = f.currentYear = c; - this._selectDate(a, this._formatDate(f, f.currentDay, f.currentMonth, f.currentYear)) - } - }, _clearDate: function (a) { - a = - d(a); - this._getInst(a[0]); - this._selectDate(a, "") - }, _selectDate: function (a, b) { - a = this._getInst(d(a)[0]); - b = b != null ? b : this._formatDate(a); - a.input && a.input.val(b); - this._updateAlternate(a); - var c = this._get(a, "onSelect"); - if (c)c.apply(a.input ? a.input[0] : null, [b, a]); else a.input && a.input.trigger("change"); - if (a.inline)this._updateDatepicker(a); else { - this._hideDatepicker(); - this._lastInput = a.input[0]; - typeof a.input[0] != "object" && a.input.focus(); - this._lastInput = null - } - }, _updateAlternate: function (a) { - var b = this._get(a, - "altField"); - if (b) { - var c = this._get(a, "altFormat") || this._get(a, "dateFormat"), e = this._getDate(a), f = this.formatDate(c, e, this._getFormatConfig(a)); - d(b).each(function () { - d(this).val(f) - }) - } - }, noWeekends: function (a) { - a = a.getDay(); - return[a > 0 && a < 6, ""] - }, iso8601Week: function (a) { - a = new Date(a.getTime()); - a.setDate(a.getDate() + 4 - (a.getDay() || 7)); - var b = a.getTime(); - a.setMonth(0); - a.setDate(1); - return Math.floor(Math.round((b - a) / 864E5) / 7) + 1 - }, parseDate: function (a, b, c) { - if (a == null || b == null)throw"Invalid arguments"; - b = typeof b == - "object" ? b.toString() : b + ""; - if (b == "")return null; - for (var e = (c ? c.shortYearCutoff : null) || this._defaults.shortYearCutoff, f = (c ? c.dayNamesShort : null) || this._defaults.dayNamesShort, h = (c ? c.dayNames : null) || this._defaults.dayNames, i = (c ? c.monthNamesShort : null) || this._defaults.monthNamesShort, g = (c ? c.monthNames : null) || this._defaults.monthNames, k = c = -1, l = -1, u = -1, j = false, o = function (p) { - (p = z + 1 < a.length && a.charAt(z + 1) == p) && z++; - return p - }, m = function (p) { - o(p); - p = new RegExp("^\\d{1," + (p == "@" ? 14 : p == "!" ? 20 : p == "y" ? 4 : p == "o" ? - 3 : 2) + "}"); - p = b.substring(s).match(p); - if (!p)throw"Missing number at position " + s; - s += p[0].length; - return parseInt(p[0], 10) - }, n = function (p, w, H) { - p = o(p) ? H : w; - for (w = 0; w < p.length; w++)if (b.substr(s, p[w].length).toLowerCase() == p[w].toLowerCase()) { - s += p[w].length; - return w + 1 - } - throw"Unknown name at position " + s; - }, r = function () { - if (b.charAt(s) != a.charAt(z))throw"Unexpected literal at position " + s; - s++ - }, s = 0, z = 0; z < a.length; z++)if (j)if (a.charAt(z) == "'" && !o("'"))j = false; else r(); else switch (a.charAt(z)) { - case "d": - l = m("d"); - break; - case "D": - n("D", f, h); - break; - case "o": - u = m("o"); - break; - case "m": - k = m("m"); - break; - case "M": - k = n("M", i, g); - break; - case "y": - c = m("y"); - break; - case "@": - var v = new Date(m("@")); - c = v.getFullYear(); - k = v.getMonth() + 1; - l = v.getDate(); - break; - case "!": - v = new Date((m("!") - this._ticksTo1970) / 1E4); - c = v.getFullYear(); - k = v.getMonth() + 1; - l = v.getDate(); - break; - case "'": - if (o("'"))r(); else j = true; - break; - default: - r() - } - if (c == -1)c = (new Date).getFullYear(); else if (c < 100)c += (new Date).getFullYear() - (new Date).getFullYear() % 100 + (c <= e ? 0 : -100); - if (u > -1) { - k = 1; - l = u; - do { - e = this._getDaysInMonth(c, k - 1); - if (l <= e)break; - k++; - l -= e - } while (1) - } - v = this._daylightSavingAdjust(new Date(c, k - 1, l)); - if (v.getFullYear() != c || v.getMonth() + 1 != k || v.getDate() != l)throw"Invalid date"; - return v - }, ATOM: "yy-mm-dd", COOKIE: "D, dd M yy", ISO_8601: "yy-mm-dd", RFC_822: "D, d M y", RFC_850: "DD, dd-M-y", RFC_1036: "D, d M y", RFC_1123: "D, d M yy", RFC_2822: "D, d M yy", RSS: "D, d M y", TICKS: "!", TIMESTAMP: "@", W3C: "yy-mm-dd", _ticksTo1970: (718685 + Math.floor(492.5) - Math.floor(19.7) + Math.floor(4.925)) * 24 * - 60 * 60 * 1E7, formatDate: function (a, b, c) { - if (!b)return""; - var e = (c ? c.dayNamesShort : null) || this._defaults.dayNamesShort, f = (c ? c.dayNames : null) || this._defaults.dayNames, h = (c ? c.monthNamesShort : null) || this._defaults.monthNamesShort; - c = (c ? c.monthNames : null) || this._defaults.monthNames; - var i = function (o) { - (o = j + 1 < a.length && a.charAt(j + 1) == o) && j++; - return o - }, g = function (o, m, n) { - m = "" + m; - if (i(o))for (; m.length < n;)m = "0" + m; - return m - }, k = function (o, m, n, r) { - return i(o) ? r[m] : n[m] - }, l = "", u = false; - if (b)for (var j = 0; j < a.length; j++)if (u)if (a.charAt(j) == - "'" && !i("'"))u = false; else l += a.charAt(j); else switch (a.charAt(j)) { - case "d": - l += g("d", b.getDate(), 2); - break; - case "D": - l += k("D", b.getDay(), e, f); - break; - case "o": - l += g("o", (b.getTime() - (new Date(b.getFullYear(), 0, 0)).getTime()) / 864E5, 3); - break; - case "m": - l += g("m", b.getMonth() + 1, 2); - break; - case "M": - l += k("M", b.getMonth(), h, c); - break; - case "y": - l += i("y") ? b.getFullYear() : (b.getYear() % 100 < 10 ? "0" : "") + b.getYear() % 100; - break; - case "@": - l += b.getTime(); - break; - case "!": - l += b.getTime() * 1E4 + this._ticksTo1970; - break; - case "'": - if (i("'"))l += - "'"; else u = true; - break; - default: - l += a.charAt(j) - } - return l - }, _possibleChars: function (a) { - for (var b = "", c = false, e = function (h) { - (h = f + 1 < a.length && a.charAt(f + 1) == h) && f++; - return h - }, f = 0; f < a.length; f++)if (c)if (a.charAt(f) == "'" && !e("'"))c = false; else b += a.charAt(f); else switch (a.charAt(f)) { - case "d": - case "m": - case "y": - case "@": - b += "0123456789"; - break; - case "D": - case "M": - return null; - case "'": - if (e("'"))b += "'"; else c = true; - break; - default: - b += a.charAt(f) - } - return b - }, _get: function (a, b) { - return a.settings[b] !== G ? a.settings[b] : this._defaults[b] - }, - _setDateFromField: function (a, b) { - if (a.input.val() != a.lastVal) { - var c = this._get(a, "dateFormat"), e = a.lastVal = a.input ? a.input.val() : null, f, h; - f = h = this._getDefaultDate(a); - var i = this._getFormatConfig(a); - try { - f = this.parseDate(c, e, i) || h - } catch (g) { - this.log(g); - e = b ? "" : e - } - a.selectedDay = f.getDate(); - a.drawMonth = a.selectedMonth = f.getMonth(); - a.drawYear = a.selectedYear = f.getFullYear(); - a.currentDay = e ? f.getDate() : 0; - a.currentMonth = e ? f.getMonth() : 0; - a.currentYear = e ? f.getFullYear() : 0; - this._adjustInstDate(a) - } - }, _getDefaultDate: function (a) { - return this._restrictMinMax(a, - this._determineDate(a, this._get(a, "defaultDate"), new Date)) - }, _determineDate: function (a, b, c) { - var e = function (h) { - var i = new Date; - i.setDate(i.getDate() + h); - return i - }, f = function (h) { - try { - return d.datepicker.parseDate(d.datepicker._get(a, "dateFormat"), h, d.datepicker._getFormatConfig(a)) - } catch (i) { - } - var g = (h.toLowerCase().match(/^c/) ? d.datepicker._getDate(a) : null) || new Date, k = g.getFullYear(), l = g.getMonth(); - g = g.getDate(); - for (var u = /([+-]?[0-9]+)\s*(d|D|w|W|m|M|y|Y)?/g, j = u.exec(h); j;) { - switch (j[2] || "d") { - case "d": - case "D": - g += - parseInt(j[1], 10); - break; - case "w": - case "W": - g += parseInt(j[1], 10) * 7; - break; - case "m": - case "M": - l += parseInt(j[1], 10); - g = Math.min(g, d.datepicker._getDaysInMonth(k, l)); - break; - case "y": - case "Y": - k += parseInt(j[1], 10); - g = Math.min(g, d.datepicker._getDaysInMonth(k, l)); - break - } - j = u.exec(h) - } - return new Date(k, l, g) - }; - if (b = (b = b == null ? c : typeof b == "string" ? f(b) : typeof b == "number" ? isNaN(b) ? c : e(b) : b) && b.toString() == "Invalid Date" ? c : b) { - b.setHours(0); - b.setMinutes(0); - b.setSeconds(0); - b.setMilliseconds(0) - } - return this._daylightSavingAdjust(b) - }, - _daylightSavingAdjust: function (a) { - if (!a)return null; - a.setHours(a.getHours() > 12 ? a.getHours() + 2 : 0); - return a - }, _setDate: function (a, b, c) { - var e = !b, f = a.selectedMonth, h = a.selectedYear; - b = this._restrictMinMax(a, this._determineDate(a, b, new Date)); - a.selectedDay = a.currentDay = b.getDate(); - a.drawMonth = a.selectedMonth = a.currentMonth = b.getMonth(); - a.drawYear = a.selectedYear = a.currentYear = b.getFullYear(); - if ((f != a.selectedMonth || h != a.selectedYear) && !c)this._notifyChange(a); - this._adjustInstDate(a); - if (a.input)a.input.val(e ? - "" : this._formatDate(a)) - }, _getDate: function (a) { - return!a.currentYear || a.input && a.input.val() == "" ? null : this._daylightSavingAdjust(new Date(a.currentYear, a.currentMonth, a.currentDay)) - }, _generateHTML: function (a) { - var b = new Date; - b = this._daylightSavingAdjust(new Date(b.getFullYear(), b.getMonth(), b.getDate())); - var c = this._get(a, "isRTL"), e = this._get(a, "showButtonPanel"), f = this._get(a, "hideIfNoPrevNext"), h = this._get(a, "navigationAsDateFormat"), i = this._getNumberOfMonths(a), g = this._get(a, "showCurrentAtPos"), k = - this._get(a, "stepMonths"), l = i[0] != 1 || i[1] != 1, u = this._daylightSavingAdjust(!a.currentDay ? new Date(9999, 9, 9) : new Date(a.currentYear, a.currentMonth, a.currentDay)), j = this._getMinMaxDate(a, "min"), o = this._getMinMaxDate(a, "max"); - g = a.drawMonth - g; - var m = a.drawYear; - if (g < 0) { - g += 12; - m-- - } - if (o) { - var n = this._daylightSavingAdjust(new Date(o.getFullYear(), o.getMonth() - i[0] * i[1] + 1, o.getDate())); - for (n = j && n < j ? j : n; this._daylightSavingAdjust(new Date(m, g, 1)) > n;) { - g--; - if (g < 0) { - g = 11; - m-- - } - } - } - a.drawMonth = g; - a.drawYear = m; - n = this._get(a, - "prevText"); - n = !h ? n : this.formatDate(n, this._daylightSavingAdjust(new Date(m, g - k, 1)), this._getFormatConfig(a)); - n = this._canAdjustMonth(a, -1, m, g) ? '' + n + "" : f ? "" : '' + - n + ""; - var r = this._get(a, "nextText"); - r = !h ? r : this.formatDate(r, this._daylightSavingAdjust(new Date(m, g + k, 1)), this._getFormatConfig(a)); - f = this._canAdjustMonth(a, +1, m, g) ? '' + r + "" : f ? "" : '' + r + ""; - k = this._get(a, "currentText"); - r = this._get(a, "gotoCurrent") && a.currentDay ? u : b; - k = !h ? k : this.formatDate(k, r, this._getFormatConfig(a)); - h = !a.inline ? '" : ""; - e = e ? '
' + (c ? h : "") + (this._isInRange(a, r) ? '" : "") + (c ? "" : h) + "
" : ""; - h = parseInt(this._get(a, "firstDay"), 10); - h = isNaN(h) ? 0 : h; - k = this._get(a, "showWeek"); - r = this._get(a, "dayNames"); - this._get(a, "dayNamesShort"); - var s = this._get(a, "dayNamesMin"), z = this._get(a, "monthNames"), v = this._get(a, "monthNamesShort"), p = this._get(a, "beforeShowDay"), w = this._get(a, "showOtherMonths"), H = this._get(a, "selectOtherMonths"); - this._get(a, "calculateWeek"); - for (var M = this._getDefaultDate(a), I = "", C = 0; C < i[0]; C++) { - for (var N = - "", D = 0; D < i[1]; D++) { - var J = this._daylightSavingAdjust(new Date(m, g, a.selectedDay)), t = " ui-corner-all", x = ""; - if (l) { - x += '
' + (/all|left/.test(t) && C == 0 ? c ? - f : n : "") + (/all|right/.test(t) && C == 0 ? c ? n : f : "") + this._generateMonthYearHeader(a, g, m, j, o, C > 0 || D > 0, z, v) + '
'; - var A = k ? '" : ""; - for (t = 0; t < 7; t++) { - var q = (t + h) % 7; - A += "= 5 ? ' class="ui-datepicker-week-end"' : "") + '>' + s[q] + "" - } - x += A + ""; - A = this._getDaysInMonth(m, g); - if (m == a.selectedYear && g == a.selectedMonth)a.selectedDay = Math.min(a.selectedDay, - A); - t = (this._getFirstDayOfMonth(m, g) - h + 7) % 7; - A = l ? 6 : Math.ceil((t + A) / 7); - q = this._daylightSavingAdjust(new Date(m, g, 1 - t)); - for (var O = 0; O < A; O++) { - x += ""; - var P = !k ? "" : '"; - for (t = 0; t < 7; t++) { - var F = p ? p.apply(a.input ? a.input[0] : null, [q]) : [true, ""], B = q.getMonth() != g, K = B && !H || !F[0] || j && q < j || o && q > o; - P += '"; - q.setDate(q.getDate() + 1); - q = this._daylightSavingAdjust(q) - } - x += P + "" - } - g++; - if (g > 11) { - g = 0; - m++ - } - x += "
' + this._get(a, "weekHeader") + "
' + this._get(a, "calculateWeek")(q) + "" + (B && !w ? " " : K ? '' + q.getDate() + - "" : '' + q.getDate() + "") + "
" + (l ? "" + (i[0] > 0 && D == i[1] - 1 ? '
' : "") : ""); - N += x - } - I += N - } - I += e + (d.browser.msie && parseInt(d.browser.version, 10) < 7 && !a.inline ? '' : - ""); - a._keyEvent = false; - return I - }, _generateMonthYearHeader: function (a, b, c, e, f, h, i, g) { - var k = this._get(a, "changeMonth"), l = this._get(a, "changeYear"), u = this._get(a, "showMonthAfterYear"), j = '
', o = ""; - if (h || !k)o += '' + i[b] + ""; else { - i = e && e.getFullYear() == c; - var m = f && f.getFullYear() == c; - o += '" - } - u || (j += o + (h || !(k && l) ? " " : "")); - if (h || !l)j += '' + c + ""; else { - g = this._get(a, "yearRange").split(":"); - var r = (new Date).getFullYear(); - i = function (s) { - s = s.match(/c[+-].*/) ? c + parseInt(s.substring(1), 10) : s.match(/[+-].*/) ? r + parseInt(s, 10) : parseInt(s, 10); - return isNaN(s) ? r : s - }; - b = i(g[0]); - g = Math.max(b, - i(g[1] || "")); - b = e ? Math.max(b, e.getFullYear()) : b; - g = f ? Math.min(g, f.getFullYear()) : g; - for (j += '" - } - j += this._get(a, "yearSuffix"); - if (u)j += (h || !(k && l) ? " " : "") + o; - j += "
"; - return j - }, _adjustInstDate: function (a, b, c) { - var e = - a.drawYear + (c == "Y" ? b : 0), f = a.drawMonth + (c == "M" ? b : 0); - b = Math.min(a.selectedDay, this._getDaysInMonth(e, f)) + (c == "D" ? b : 0); - e = this._restrictMinMax(a, this._daylightSavingAdjust(new Date(e, f, b))); - a.selectedDay = e.getDate(); - a.drawMonth = a.selectedMonth = e.getMonth(); - a.drawYear = a.selectedYear = e.getFullYear(); - if (c == "M" || c == "Y")this._notifyChange(a) - }, _restrictMinMax: function (a, b) { - var c = this._getMinMaxDate(a, "min"); - a = this._getMinMaxDate(a, "max"); - b = c && b < c ? c : b; - return b = a && b > a ? a : b - }, _notifyChange: function (a) { - var b = this._get(a, - "onChangeMonthYear"); - if (b)b.apply(a.input ? a.input[0] : null, [a.selectedYear, a.selectedMonth + 1, a]) - }, _getNumberOfMonths: function (a) { - a = this._get(a, "numberOfMonths"); - return a == null ? [1, 1] : typeof a == "number" ? [1, a] : a - }, _getMinMaxDate: function (a, b) { - return this._determineDate(a, this._get(a, b + "Date"), null) - }, _getDaysInMonth: function (a, b) { - return 32 - (new Date(a, b, 32)).getDate() - }, _getFirstDayOfMonth: function (a, b) { - return(new Date(a, b, 1)).getDay() - }, _canAdjustMonth: function (a, b, c, e) { - var f = this._getNumberOfMonths(a); - c = this._daylightSavingAdjust(new Date(c, e + (b < 0 ? b : f[0] * f[1]), 1)); - b < 0 && c.setDate(this._getDaysInMonth(c.getFullYear(), c.getMonth())); - return this._isInRange(a, c) - }, _isInRange: function (a, b) { - var c = this._getMinMaxDate(a, "min"); - a = this._getMinMaxDate(a, "max"); - return(!c || b.getTime() >= c.getTime()) && (!a || b.getTime() <= a.getTime()) - }, _getFormatConfig: function (a) { - var b = this._get(a, "shortYearCutoff"); - b = typeof b != "string" ? b : (new Date).getFullYear() % 100 + parseInt(b, 10); - return{shortYearCutoff: b, dayNamesShort: this._get(a, - "dayNamesShort"), dayNames: this._get(a, "dayNames"), monthNamesShort: this._get(a, "monthNamesShort"), monthNames: this._get(a, "monthNames")} - }, _formatDate: function (a, b, c, e) { - if (!b) { - a.currentDay = a.selectedDay; - a.currentMonth = a.selectedMonth; - a.currentYear = a.selectedYear - } - b = b ? typeof b == "object" ? b : this._daylightSavingAdjust(new Date(e, c, b)) : this._daylightSavingAdjust(new Date(a.currentYear, a.currentMonth, a.currentDay)); - return this.formatDate(this._get(a, "dateFormat"), b, this._getFormatConfig(a)) - }}); - d.fn.datepicker = - function (a) { - if (!d.datepicker.initialized) { - d(document).mousedown(d.datepicker._checkExternalClick).find("body").append(d.datepicker.dpDiv); - d.datepicker.initialized = true - } - var b = Array.prototype.slice.call(arguments, 1); - if (typeof a == "string" && (a == "isDisabled" || a == "getDate" || a == "widget"))return d.datepicker["_" + a + "Datepicker"].apply(d.datepicker, [this[0]].concat(b)); - if (a == "option" && arguments.length == 2 && typeof arguments[1] == "string")return d.datepicker["_" + a + "Datepicker"].apply(d.datepicker, [this[0]].concat(b)); - return this.each(function () { - typeof a == "string" ? d.datepicker["_" + a + "Datepicker"].apply(d.datepicker, [this].concat(b)) : d.datepicker._attachDatepicker(this, a) - }) - }; - d.datepicker = new L; - d.datepicker.initialized = false; - d.datepicker.uuid = (new Date).getTime(); - d.datepicker.version = "1.8.5"; - window["DP_jQuery_" + y] = d -})(jQuery); -; -/* - * jQuery UI Progressbar 1.8.5 - * - * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Progressbar - * - * Depends: - * jquery.ui.core.js - * jquery.ui.widget.js - */ -(function (b, c) { - b.widget("ui.progressbar", {options: {value: 0}, min: 0, max: 100, _create: function () { - this.element.addClass("ui-progressbar ui-widget ui-widget-content ui-corner-all").attr({role: "progressbar", "aria-valuemin": this.min, "aria-valuemax": this.max, "aria-valuenow": this._value()}); - this.valueDiv = b("
").appendTo(this.element); - this._refreshValue() - }, destroy: function () { - this.element.removeClass("ui-progressbar ui-widget ui-widget-content ui-corner-all").removeAttr("role").removeAttr("aria-valuemin").removeAttr("aria-valuemax").removeAttr("aria-valuenow"); - this.valueDiv.remove(); - b.Widget.prototype.destroy.apply(this, arguments) - }, value: function (a) { - if (a === c)return this._value(); - this._setOption("value", a); - return this - }, _setOption: function (a, d) { - if (a === "value") { - this.options.value = d; - this._refreshValue(); - this._trigger("change") - } - b.Widget.prototype._setOption.apply(this, arguments) - }, _value: function () { - var a = this.options.value; - if (typeof a !== "number")a = 0; - return Math.min(this.max, Math.max(this.min, a)) - }, _refreshValue: function () { - var a = this.value(); - this.valueDiv.toggleClass("ui-corner-right", - a === this.max).width(a + "%"); - this.element.attr("aria-valuenow", a) - }}); - b.extend(b.ui.progressbar, {version: "1.8.5"}) -})(jQuery); -; \ No newline at end of file diff --git a/wagtail/vendor/django-treebeard/treebeard/static/treebeard/treebeard-admin.css b/wagtail/vendor/django-treebeard/treebeard/static/treebeard/treebeard-admin.css deleted file mode 100644 index dba330b58..000000000 --- a/wagtail/vendor/django-treebeard/treebeard/static/treebeard/treebeard-admin.css +++ /dev/null @@ -1,83 +0,0 @@ -/* Treebeard Admin */ - -#roots { - margin: 0; - padding: 0; -} - -#roots li { - list-style: none; - padding: 5px !important; - line-height: 13px; - border-bottom: 1px solid #EEE; -} - -#roots li a { - font-weight: bold; - font-size: 12px; -} - -#roots li input { - margin: 0 5px; -} - -.oder-grabber { - width: 1.5em; - text-align: center; -} - -.drag-handler span { - width: 16px; - background: transparent url(expand-collapse.png) no-repeat left -48px; - height: 16px; - margin: 0 5px; - display: inline-block; -} - -.drag-handler span.active { - background: transparent url(expand-collapse.png) no-repeat left -32px; - cursor: move; -} - -.spacer { - width: 10px; - margin: 0 10px; -} - -.collapse { - width: 16px; - height: 16px; - display: inline-block; - text-indent: -999px; -} - -.collapsed { - background: transparent url(expand-collapse.png) no-repeat left -16px; -} - -.expanded { - background: transparent url(expand-collapse.png) no-repeat left 0; -} - -#drag_line { - border-top: 5px solid #A0A; - background: #A0A; - display: block; - position: absolute; -} - -#drag_line span { - position: relative; - display: block; - width: 100px; - background: #FFD; - color: #000; - left: 100px; - text-align: center; - border: 1px solid #000; - vertical-align: center; -} - -/*tr:target { I'm handling the highlight with js to have more control -background-color: #FF0; -}*/ diff --git a/wagtail/vendor/django-treebeard/treebeard/static/treebeard/treebeard-admin.js b/wagtail/vendor/django-treebeard/treebeard/static/treebeard/treebeard-admin.js deleted file mode 100644 index b8daa6b68..000000000 --- a/wagtail/vendor/django-treebeard/treebeard/static/treebeard/treebeard-admin.js +++ /dev/null @@ -1,314 +0,0 @@ -(function ($) { -// Ok, let's do eeet - - ACTIVE_NODE_BG_COLOR = '#B7D7E8'; - RECENTLY_MOVED_COLOR = '#FFFF00'; - RECENTLY_MOVED_FADEOUT = '#FFFFFF'; - ABORT_COLOR = '#EECCCC'; - DRAG_LINE_COLOR = '#AA00AA'; - - RECENTLY_FADE_DURATION = 2000; - -// This is the basic Node class, which handles UI tree operations for each 'row' - var Node = function (elem) { - var $elem = $(elem); - var node_id = $elem.attr('node'); - var parent_id = $elem.attr('parent'); - var level = parseInt($elem.attr('level')); - var children_num = parseInt($elem.attr('children-num')); - return { - elem: elem, - $elem: $elem, - node_id: node_id, - parent_id: parent_id, - level: level, - has_children: function () { - return children_num > 0; - }, - node_name: function () { - // Returns the text of the node - return $elem.find('th a:not(.collapse)').text(); - }, - is_collapsed: function () { - return $elem.find('a.collapse').hasClass('collapsed'); - }, - children: function () { - return $('tr[parent=' + node_id + ']'); - }, - collapse: function () { - // For each children, hide it's childrens and so on... - $.each(this.children(),function () { - var node = new Node(this); - node.collapse(); - }).hide(); - // Swicth class to set the proprt expand/collapse icon - $elem.find('a.collapse').removeClass('expanded').addClass('collapsed'); - }, - parent_node: function () { - // Returns a Node object of the parent - return new Node($('tr[node=' + parent_id + ']', $elem.parent())[0]); - }, - expand: function () { - // Display each kid (will display in collapsed state) - this.children().show(); - // Swicth class to set the proprt expand/collapse icon - $elem.find('a.collapse').removeClass('collapsed').addClass('expanded'); - - }, - toggle: function () { - if (this.is_collapsed()) { - this.expand(); - } else { - this.collapse(); - } - }, - clone: function () { - return $elem.clone(); - } - } - }; - - $(document).ready(function () { - - // begin csrf token code - // Taken from http://docs.djangoproject.com/en/dev/ref/contrib/csrf/#ajax - $(document).ajaxSend(function (event, xhr, settings) { - function getCookie(name) { - var cookieValue = null; - if (document.cookie && document.cookie != '') { - var cookies = document.cookie.split(';'); - for (var i = 0; i < cookies.length; i++) { - var cookie = jQuery.trim(cookies[i]); - // Does this cookie string begin with the name we want? - if (cookie.substring(0, name.length + 1) == (name + '=')) { - cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); - break; - } - } - } - return cookieValue; - } - - if (!(/^http:.*/.test(settings.url) || /^https:.*/.test(settings.url))) { - // Only send the token to relative URLs i.e. locally. - xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken')); - } - }); - // end csrf token code - - - // Don't activate drag or collapse if GET filters are set on the page - if ($('#has-filters').val() === "1") { - return; - } - - $body = $('body'); - - // Activate all rows for drag & drop - // then bind mouse down event - $('td.drag-handler span').addClass('active').bind('mousedown', function (evt) { - $ghost = $('
'); - $drag_line = $('
'); - $ghost.appendTo($body); - $drag_line.appendTo($body); - - var stop_drag = function () { - $ghost.remove(); - $drag_line.remove(); - $body.enableSelection().unbind('mousemove').unbind('mouseup'); - node.elem.removeAttribute('style'); - }; - - // Create a clone create the illusion that we're moving the node - var node = new Node($(this).closest('tr')[0]); - cloned_node = node.clone(); - node.$elem.css({ - 'background': ACTIVE_NODE_BG_COLOR - }); - - $targetRow = null; - as_child = false; - - // Now make the new clone move with the mouse - $body.disableSelection().bind('mousemove',function (evt2) { - $ghost.html(cloned_node).css({ // from FeinCMS :P - 'opacity': .8, - 'position': 'absolute', - 'top': evt2.pageY, - 'left': evt2.pageX - 30, - 'width': 600 - }); - // Iterate through all rows and see where am I moving so I can place - // the drag line accordingly - rowHeight = node.$elem.height(); - $('tr', node.$elem.parent()).each(function (index, element) { - $row = $(element); - rtop = $row.offset().top; - // The tooltop will display whether I'm droping the element as - // child or sibling - $tooltip = $drag_line.find('span'); - $tooltip.css({ - 'left': node.$elem.width() - $tooltip.width(), - 'height': rowHeight, - }); - node_top = node.$elem.offset().top; - // Check if you are dragging over the same node - if (evt2.pageY >= node_top && evt2.pageY <= node_top + rowHeight) { - $targetRow = null; - $tooltip.text(gettext('Abort')); - $drag_line.css({ - 'top': node_top, - 'height': rowHeight, - 'borderWidth': 0, - 'opacity': 0.8, - 'backgroundColor': ABORT_COLOR - }); - } else - // Check if mouse is over this row - if (evt2.pageY >= rtop && evt2.pageY <= rtop + rowHeight / 2) { - // The mouse is positioned on the top half of a $row - $targetRow = $row; - as_child = false; - $drag_line.css({ - 'left': node.$elem.offset().left, - 'width': node.$elem.width(), - 'top': rtop, - 'borderWidth': '5px', - 'height': 0, - 'opacity': 1 - }); - $tooltip.text(gettext('As Sibling')); - } else if (evt2.pageY >= rtop + rowHeight / 2 && evt2.pageY <= rtop + rowHeight) { - // The mouse is positioned on the bottom half of a row - $targetRow = $row; - target_node = new Node($targetRow[0]); - if (target_node.is_collapsed()) { - target_node.expand(); - } - as_child = true; - $drag_line.css({ - 'top': rtop, - 'left': node.$elem.offset().left, - 'height': rowHeight, - 'opacity': 0.4, - 'width': node.$elem.width(), - 'borderWidth': 0, - 'backgroundColor': DRAG_LINE_COLOR - }); - $tooltip.text(gettext('As child')); - } - }); - }).bind('mouseup',function () { - if ($targetRow !== null) { - target_node = new Node($targetRow[0]); - if (target_node.node_id !== node.node_id) { - /*alert('Insert node ' + node.node_name() + ' as child of: ' - + target_node.parent_node().node_name() + '\n and sibling of: ' - + target_node.node_name());*/ - // Call $.ajax so we can handle the error - // On Drop, make an XHR call to perform the node move - $.ajax({ - url: window.MOVE_NODE_ENDPOINT, - type: 'POST', - data: { - node_id: node.node_id, - parent_id: target_node.parent_id, - sibling_id: target_node.node_id, - as_child: as_child ? 1 : 0 - }, - complete: function (req, status) { - // http://stackoverflow.com/questions/1439895/add-a-hash-with-javascript-to-url-without-scrolling-page/1439910#1439910 - node.$elem.remove(); - window.location.hash = 'node-' + node.node_id; - window.location.reload(); - }, - error: function (req, status, error) { - // On error (!200) also reload to display - // the message - node.$elem.remove(); - window.location.hash = 'node-' + node.node_id; - window.location.reload(); - } - }); - } - } - stop_drag(); - }).bind('keyup', function (kbevt) { - // Cancel drag on escape - if (kbevt.keyCode === 27) { - stop_drag(); - } - }); - }); - - $('a.collapse').click(function () { - var node = new Node($(this).closest('tr')[0]); // send the DOM node, not jQ - node.toggle(); - return false; - }); - var hash = window.location.hash; - // This is a hack, the actual element's id ends in '-id' but the url's hash - // doesn't, I'm doing this to avoid scrolling the page... is that a good thing? - if (hash) { - $(hash + '-id').animate({ - backgroundColor: RECENTLY_MOVED_COLOR - }, RECENTLY_FADE_DURATION, function () { - $(this).animate({ - backgroundColor: RECENTLY_MOVED_FADEOUT - }, RECENTLY_FADE_DURATION, function () { - this.removeAttribute('style'); - }); - }); - } - }); -})(django.jQuery); - -// http://stackoverflow.com/questions/190560/jquery-animate-backgroundcolor/2302005#2302005 -(function (d) { - d.each(["backgroundColor", "borderBottomColor", "borderLeftColor", "borderRightColor", "borderTopColor", "color", "outlineColor"], function (f, e) { - d.fx.step[e] = function (g) { - if (!g.colorInit) { - g.start = c(g.elem, e); - g.end = b(g.end); - g.colorInit = true - } - g.elem.style[e] = "rgb(" + [Math.max(Math.min(parseInt((g.pos * (g.end[0] - g.start[0])) + g.start[0]), 255), 0), Math.max(Math.min(parseInt((g.pos * (g.end[1] - g.start[1])) + g.start[1]), 255), 0), Math.max(Math.min(parseInt((g.pos * (g.end[2] - g.start[2])) + g.start[2]), 255), 0)].join(",") + ")" - } - }); - function b(f) { - var e; - if (f && f.constructor == Array && f.length == 3) { - return f - } - if (e = /rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(f)) { - return[parseInt(e[1]), parseInt(e[2]), parseInt(e[3])] - } - if (e = /rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(f)) { - return[parseFloat(e[1]) * 2.55, parseFloat(e[2]) * 2.55, parseFloat(e[3]) * 2.55] - } - if (e = /#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(f)) { - return[parseInt(e[1], 16), parseInt(e[2], 16), parseInt(e[3], 16)] - } - if (e = /#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(f)) { - return[parseInt(e[1] + e[1], 16), parseInt(e[2] + e[2], 16), parseInt(e[3] + e[3], 16)] - } - if (e = /rgba\(0, 0, 0, 0\)/.exec(f)) { - return a.transparent - } - return a[d.trim(f).toLowerCase()] - } - - function c(g, e) { - var f; - do { - f = d.css(g, e); - if (f != "" && f != "transparent" || d.nodeName(g, "body")) { - break - } - e = "backgroundColor" - } while (g = g.parentNode); - return b(f) - } - - var a = {aqua: [0, 255, 255], azure: [240, 255, 255], beige: [245, 245, 220], black: [0, 0, 0], blue: [0, 0, 255], brown: [165, 42, 42], cyan: [0, 255, 255], darkblue: [0, 0, 139], darkcyan: [0, 139, 139], darkgrey: [169, 169, 169], darkgreen: [0, 100, 0], darkkhaki: [189, 183, 107], darkmagenta: [139, 0, 139], darkolivegreen: [85, 107, 47], darkorange: [255, 140, 0], darkorchid: [153, 50, 204], darkred: [139, 0, 0], darksalmon: [233, 150, 122], darkviolet: [148, 0, 211], fuchsia: [255, 0, 255], gold: [255, 215, 0], green: [0, 128, 0], indigo: [75, 0, 130], khaki: [240, 230, 140], lightblue: [173, 216, 230], lightcyan: [224, 255, 255], lightgreen: [144, 238, 144], lightgrey: [211, 211, 211], lightpink: [255, 182, 193], lightyellow: [255, 255, 224], lime: [0, 255, 0], magenta: [255, 0, 255], maroon: [128, 0, 0], navy: [0, 0, 128], olive: [128, 128, 0], orange: [255, 165, 0], pink: [255, 192, 203], purple: [128, 0, 128], violet: [128, 0, 128], red: [255, 0, 0], silver: [192, 192, 192], white: [255, 255, 255], yellow: [255, 255, 0], transparent: [255, 255, 255]} -})(django.jQuery); diff --git a/wagtail/vendor/django-treebeard/treebeard/templates/admin/tree_change_list.html b/wagtail/vendor/django-treebeard/treebeard/templates/admin/tree_change_list.html deleted file mode 100644 index d98e819ca..000000000 --- a/wagtail/vendor/django-treebeard/treebeard/templates/admin/tree_change_list.html +++ /dev/null @@ -1,23 +0,0 @@ -{# Used for MP and NS trees #} -{% extends "admin/change_list.html" %} -{% load admin_list admin_tree i18n %} - -{% block extrastyle %} - {{ block.super }} - {% treebeard_css %} -{% endblock %} - -{% block extrahead %} - {{ block.super }} - {% treebeard_js %} -{% endblock %} - -{% block result_list %} - {% if action_form and actions_on_top and cl.full_result_count %} - {% admin_actions %} - {% endif %} - {% result_tree cl request %} - {% if action_form and actions_on_bottom and cl.full_result_count %} - {% admin_actions %} - {% endif %} -{% endblock %} diff --git a/wagtail/vendor/django-treebeard/treebeard/templates/admin/tree_change_list_results.html b/wagtail/vendor/django-treebeard/treebeard/templates/admin/tree_change_list_results.html deleted file mode 100644 index 716e6db82..000000000 --- a/wagtail/vendor/django-treebeard/treebeard/templates/admin/tree_change_list_results.html +++ /dev/null @@ -1,38 +0,0 @@ -{% if result_hidden_fields %} -
{# DIV for HTML validation #} - {% for item in result_hidden_fields %}{{ item }}{% endfor %} -
-{% endif %} -{% if results %} - - - - {% for header in result_headers %} - - {% if header.sortable %}{% endif %} - {{ header.text|capfirst }} - {% if header.sortable %}{% endif %}{% endfor %} - - - - {% for node_id, parent_id, node_level, has_children, result in results %} - - {% for item in result %} - {% if forloop.counter == 1 %} - {% for spacer in item.depth %}  - {% endfor %} - {% endif %} - {{ item }} - {% endfor %} - {% endfor %} - -
- - -{% endif %} - diff --git a/wagtail/vendor/django-treebeard/treebeard/templates/admin/tree_list.html b/wagtail/vendor/django-treebeard/treebeard/templates/admin/tree_list.html deleted file mode 100644 index 9a7a27ff2..000000000 --- a/wagtail/vendor/django-treebeard/treebeard/templates/admin/tree_list.html +++ /dev/null @@ -1,21 +0,0 @@ -{# Used for AL trees #} -{% extends "admin/change_list.html" %} -{% load admin_list admin_tree_list i18n %} - -{% block extrastyle %} - {{ block.super }} -{% endblock %} - -{% block extrahead %} - {{ block.super }} -{% endblock %} - -{% block result_list %} - {% if action_form and actions_on_top and cl.full_result_count %} - {% admin_actions %} - {% endif %} - {% result_tree cl request %} - {% if action_form and actions_on_bottom and cl.full_result_count %} - {% admin_actions %} - {% endif %} -{% endblock %} diff --git a/wagtail/vendor/django-treebeard/treebeard/templates/admin/tree_list_results.html b/wagtail/vendor/django-treebeard/treebeard/templates/admin/tree_list_results.html deleted file mode 100644 index a4fe2c225..000000000 --- a/wagtail/vendor/django-treebeard/treebeard/templates/admin/tree_list_results.html +++ /dev/null @@ -1,7 +0,0 @@ -{% if results %} -
    - {% for result in results %} -
  • {{ result }}
  • - {% endfor %} -
-{% endif %} diff --git a/wagtail/vendor/django-treebeard/treebeard/templatetags/__init__.py b/wagtail/vendor/django-treebeard/treebeard/templatetags/__init__.py deleted file mode 100644 index 217335b91..000000000 --- a/wagtail/vendor/django-treebeard/treebeard/templatetags/__init__.py +++ /dev/null @@ -1,51 +0,0 @@ -import datetime -import decimal - -from django.template import Variable, VariableDoesNotExist -from django.utils import formats, timezone, six -from django.utils.encoding import smart_text -from django.utils.html import conditional_escape -from django.utils.safestring import mark_safe - -action_form_var = Variable('action_form') - - -def needs_checkboxes(context): - try: - return action_form_var.resolve(context) is not None - except VariableDoesNotExist: - return False - - -def display_for_value(value, boolean=False): # pragma: no cover - """ Added for compatibility with django 1.4, copied from django trunk. - """ - from django.contrib.admin.templatetags.admin_list import _boolean_icon - from django.contrib.admin.views.main import EMPTY_CHANGELIST_VALUE - - if boolean: - return _boolean_icon(value) - elif value is None: - return EMPTY_CHANGELIST_VALUE - elif isinstance(value, datetime.datetime): - return formats.localize(timezone.template_localtime(value)) - elif isinstance(value, (datetime.date, datetime.time)): - return formats.localize(value) - elif isinstance(value, six.integer_types + (decimal.Decimal, float)): - return formats.number_format(value) - else: - return smart_text(value) - - -def format_html(format_string, *args, **kwargs): # pragma: no cover - """ - Added for compatibility with django 1.4, copied from django trunk. - - Similar to str.format, but passes all arguments through conditional_escape, - and calls 'mark_safe' on the result. This function should be used instead - of str.format or % interpolation to build up small HTML fragments. - """ - args_safe = map(conditional_escape, args) - kwargs_safe = dict([(k, conditional_escape(v)) for (k, v) in - six.iteritems(kwargs)]) - return mark_safe(format_string.format(*args_safe, **kwargs_safe)) \ No newline at end of file diff --git a/wagtail/vendor/django-treebeard/treebeard/templatetags/admin_tree.py b/wagtail/vendor/django-treebeard/treebeard/templatetags/admin_tree.py deleted file mode 100644 index 17234788b..000000000 --- a/wagtail/vendor/django-treebeard/treebeard/templatetags/admin_tree.py +++ /dev/null @@ -1,279 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Templatetags for django-treebeard to add drag and drop capabilities to the -nodes change list - @jjdelc - -""" - -import datetime -import sys - -from django.db import models -from django.conf import settings -from django.contrib.admin.templatetags.admin_list import ( - result_headers, result_hidden_fields) -from django.contrib.admin.util import lookup_field, display_for_field -from django.contrib.admin.views.main import EMPTY_CHANGELIST_VALUE -from django.core.exceptions import ObjectDoesNotExist -from django.template import Library -from django.utils.html import conditional_escape -from django.utils.safestring import mark_safe -from django.utils.translation import ugettext_lazy as _ - - -if sys.version < '3': - import codecs - - def u(x): - return codecs.unicode_escape_decode(x)[0] -else: - def u(x): - return x - -register = Library() - -if sys.version_info >= (3, 0): - from django.utils.encoding import force_str, smart_str - from urllib.parse import urljoin -else: - from django.utils.encoding import force_unicode as force_str - from django.utils.encoding import smart_unicode as smart_str - from urlparse import urljoin - - -try: - from django.contrib.admin.util import display_for_value - from django.utils.html import format_html -except ImportError: - from treebeard.templatetags import display_for_value, format_html - -from treebeard.templatetags import needs_checkboxes - - -def get_result_and_row_class(cl, field_name, result): - row_class = '' - try: - f, attr, value = lookup_field(field_name, result, cl.model_admin) - except ObjectDoesNotExist: - result_repr = EMPTY_CHANGELIST_VALUE - else: - if f is None: - if field_name == 'action_checkbox': - row_class = mark_safe(' class="action-checkbox"') - allow_tags = getattr(attr, 'allow_tags', False) - boolean = getattr(attr, 'boolean', False) - if boolean: - allow_tags = True - result_repr = display_for_value(value, boolean) - # Strip HTML tags in the resulting text, except if the - # function has an "allow_tags" attribute set to True. - if allow_tags: - result_repr = mark_safe(result_repr) - if isinstance(value, (datetime.date, datetime.time)): - row_class = mark_safe(' class="nowrap"') - else: - if isinstance(f.rel, models.ManyToOneRel): - field_val = getattr(result, f.name) - if field_val is None: - result_repr = EMPTY_CHANGELIST_VALUE - else: - result_repr = field_val - else: - result_repr = display_for_field(value, f) - if isinstance(f, (models.DateField, models.TimeField, - models.ForeignKey)): - row_class = mark_safe(' class="nowrap"') - if force_str(result_repr) == '': - result_repr = mark_safe(' ') - return result_repr, row_class - - -def get_spacer(first, result): - if first: - spacer = ' ' * ( - result.get_depth() - 1) - else: - spacer = '' - - return spacer - - -def get_collapse(result): - if result.get_children_count(): - collapse = ('' - '-') - else: - collapse = ' ' - - return collapse - - -def get_drag_handler(first): - drag_handler = '' - if first: - drag_handler = ('' - ' ') - return drag_handler - - -def items_for_result(cl, result, form): - """ - Generates the actual list of data. - - @jjdelc: - This has been shamelessly copied from original - django.contrib.admin.templatetags.admin_list.items_for_result - in order to alter the dispay for the first element - """ - first = True - pk = cl.lookup_opts.pk.attname - for field_name in cl.list_display: - result_repr, row_class = get_result_and_row_class(cl, field_name, - result) - # If list_display_links not defined, add the link tag to the - # first field - if (first and not cl.list_display_links) or \ - field_name in cl.list_display_links: - table_tag = {True: 'th', False: 'td'}[first] - # This spacer indents the nodes based on their depth - spacer = get_spacer(first, result) - # This shows a collapse or expand link for nodes with childs - collapse = get_collapse(result) - # Add a before the first col to show the drag handler - drag_handler = get_drag_handler(first) - first = False - url = cl.url_for_result(result) - # Convert the pk to something that can be used in Javascript. - # Problem cases are long ints (23L) and non-ASCII strings. - if cl.to_field: - attr = str(cl.to_field) - else: - attr = pk - value = result.serializable_value(attr) - result_id = repr(force_str(value))[1:] - onclickstr = ( - ' onclick="opener.dismissRelatedLookupPopup(window, %s);' - ' return false;"') - yield mark_safe( - u('%s<%s%s>%s %s %s') % ( - drag_handler, table_tag, row_class, spacer, collapse, url, - (cl.is_popup and onclickstr % result_id or ''), - conditional_escape(result_repr), table_tag)) - else: - # By default the fields come from ModelAdmin.list_editable, but if - # we pull the fields out of the form instead of list_editable - # custom admins can provide fields on a per request basis - if (form and field_name in form.fields and not ( - field_name == cl.model._meta.pk.name and - form[cl.model._meta.pk.name].is_hidden)): - bf = form[field_name] - result_repr = mark_safe(force_str(bf.errors) + force_str(bf)) - yield format_html(u('{1}'), row_class, result_repr) - if form and not form[cl.model._meta.pk.name].is_hidden: - yield format_html(u('{0}'), - force_str(form[cl.model._meta.pk.name])) - - -def get_parent_id(node): - """Return the node's parent id or 0 if node is a root node.""" - if node.is_root(): - return 0 - return node.get_parent().pk - - -def results(cl): - if cl.formset: - for res, form in zip(cl.result_list, cl.formset.forms): - yield (res.pk, get_parent_id(res), res.get_depth(), - res.get_children_count(), - list(items_for_result(cl, res, form))) - else: - for res in cl.result_list: - yield (res.pk, get_parent_id(res), res.get_depth(), - res.get_children_count(), - list(items_for_result(cl, res, None))) - - -def check_empty_dict(GET_dict): - """ - Returns True if the GET querstring contains on values, but it can contain - empty keys. - This is better than doing not bool(request.GET) as an empty key will return - True - """ - empty = True - for k, v in GET_dict.items(): - # Don't disable on p(age) or 'all' GET param - if v and k != 'p' and k != 'all': - empty = False - return empty - - -@register.inclusion_tag( - 'admin/tree_change_list_results.html', takes_context=True) -def result_tree(context, cl, request): - """ - Added 'filtered' param, so the template's js knows whether the results have - been affected by a GET param or not. Only when the results are not filtered - you can drag and sort the tree - """ - - # Here I'm adding an extra col on pos 2 for the drag handlers - headers = list(result_headers(cl)) - headers.insert(1 if needs_checkboxes(context) else 0, { - 'text': '+', - 'sortable': True, - 'url': request.path, - 'tooltip': _('Return to ordered tree'), - 'class_attrib': mark_safe(' class="oder-grabber"') - }) - return { - 'filtered': not check_empty_dict(request.GET), - 'result_hidden_fields': list(result_hidden_fields(cl)), - 'result_headers': headers, - 'results': list(results(cl)), - } - - -def get_static_url(): - """Return a base static url, always ending with a /""" - path = getattr(settings, 'STATIC_URL', None) - if not path: - path = getattr(settings, 'MEDIA_URL', None) - if not path: - path = '/' - return path - - -@register.simple_tag -def treebeard_css(): - """ - Template tag to print out the proper tag to include a custom .css - """ - LINK_HTML = """""" - css_file = urljoin(get_static_url(), 'treebeard/treebeard-admin.css') - return LINK_HTML % css_file - - -@register.simple_tag -def treebeard_js(): - """ - Template tag to print out the proper """ - js_file = '/'.join([path.rstrip('/'), 'treebeard', 'treebeard-admin.js']) - - # Jquery UI is needed to call disableSelection() on drag and drop so - # text selections arent marked while dragging a table row - # http://www.lokkju.com/blog/archives/143 - JQUERY_UI = ("" - "") - jquery_ui = urljoin(path, 'treebeard/jquery-ui-1.8.5.custom.min.js') - - scripts = [SCRIPT_HTML % 'jsi18n', - SCRIPT_HTML % js_file, - JQUERY_UI % jquery_ui] - return ''.join(scripts) diff --git a/wagtail/vendor/django-treebeard/treebeard/templatetags/admin_tree_list.py b/wagtail/vendor/django-treebeard/treebeard/templatetags/admin_tree_list.py deleted file mode 100644 index 9272dd60a..000000000 --- a/wagtail/vendor/django-treebeard/treebeard/templatetags/admin_tree_list.py +++ /dev/null @@ -1,40 +0,0 @@ -# -*- coding: utf-8 -*- - -from django.template import Library -from treebeard.templatetags import needs_checkboxes - - -register = Library() -CHECKBOX_TMPL = ('') - - -def _line(context, node, request): - if 't' in request.GET and request.GET['t'] == 'id': - raw_id_fields = """ - onclick="opener.dismissRelatedLookupPopup(window, '%d'); return false;" - """ % (node.pk,) - else: - raw_id_fields = '' - output = '' - if needs_checkboxes(context): - output += CHECKBOX_TMPL % node.pk - return output + '%s' % ( - node.pk, raw_id_fields, str(node)) - - -def _subtree(context, node, request): - tree = '' - for subnode in node.get_children(): - tree += '
  • %s
  • ' % _subtree(context, subnode, request) - if tree: - tree = '
      %s
    ' % tree - return _line(context, node, request) + tree - - -@register.simple_tag(takes_context=True) -def result_tree(context, cl, request): - tree = '' - for root_node in cl.model.get_root_nodes(): - tree += '
  • %s
  • ' % _subtree(context, root_node, request) - return "
      %s
    " % tree diff --git a/wagtail/vendor/django-treebeard/treebeard/tests/__init__.py b/wagtail/vendor/django-treebeard/treebeard/tests/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/wagtail/vendor/django-treebeard/treebeard/tests/admin.py b/wagtail/vendor/django-treebeard/treebeard/tests/admin.py deleted file mode 100644 index d673b9f17..000000000 --- a/wagtail/vendor/django-treebeard/treebeard/tests/admin.py +++ /dev/null @@ -1,21 +0,0 @@ -import datetime - -from django.contrib import admin -from treebeard.admin import admin_factory -from treebeard.forms import movenodeform_factory - -from treebeard.tests.models import BASE_MODELS, UNICODE_MODELS - - -def register(model): - form_class = movenodeform_factory(model) - admin_class = admin_factory(form_class) - admin.site.register(model, admin_class) - - -for model in BASE_MODELS: - register(model) - - -for model in UNICODE_MODELS: - register(model) diff --git a/wagtail/vendor/django-treebeard/treebeard/tests/conftest.py b/wagtail/vendor/django-treebeard/treebeard/tests/conftest.py deleted file mode 100644 index cf79cf5b6..000000000 --- a/wagtail/vendor/django-treebeard/treebeard/tests/conftest.py +++ /dev/null @@ -1,80 +0,0 @@ -import os -import sys -import time - - -os.environ['DJANGO_SETTINGS_MODULE'] = 'treebeard.tests.settings' - -import django -from django.conf import settings -from django.test.utils import (setup_test_environment, - teardown_test_environment) -from django.test.client import Client -from django.core.management import call_command -from django.core import mail -from django.db import connection -from django.db.models.base import ModelBase -from _pytest import python as _pytest_python - - -def idmaker(argnames, argvalues): - idlist = [] - for valindex, valset in enumerate(argvalues): - this_id = [] - for nameindex, val in enumerate(valset): - argname = argnames[nameindex] - if isinstance(val, (float, int, str)): - this_id.append(str(val)) - elif isinstance(val, ModelBase): - this_id.append(val.__name__) - else: - this_id.append("{0}-{1}={2!s}".format(argname, valindex)) - idlist.append("][".join(this_id)) - return idlist -_pytest_python.idmaker = idmaker - - -def pytest_report_header(config): - return 'Django: ' + django.get_version() - - -def pytest_configure(config): - setup_test_environment() - connection.creation.create_test_db(verbosity=2, autoclobber=True) - - -def pytest_unconfigure(config): - dbsettings = settings.DATABASES['default'] - dbtestname = dbsettings['TEST_NAME'] - connection.close() - if dbsettings['ENGINE'].split('.')[-1] == 'postgresql_psycopg2': - connection.connection = None - connection.settings_dict['NAME'] = dbtestname.split('_')[1] - cursor = connection.cursor() - connection.autocommit = True - if django.VERSION < (1, 6): - connection._set_isolation_level(0) - else: - connection._set_autocommit(True) - time.sleep(1) - sys.stdout.write( - "Destroying test database for alias '%s' (%s)...\n" % ( - connection.alias, dbtestname) - ) - sys.stdout.flush() - cursor.execute( - 'DROP DATABASE %s' % connection.ops.quote_name(dbtestname)) - else: - connection.creation.destroy_test_db(dbtestname, verbosity=2) - teardown_test_environment() - - -def pytest_funcarg__client(request): - def setup(): - mail.outbox = [] - return Client() - - def teardown(client): - call_command('flush', verbosity=0, interactive=False) - - return request.cached_setup(setup, teardown, 'function') diff --git a/wagtail/vendor/django-treebeard/treebeard/tests/jenkins/quality.sh b/wagtail/vendor/django-treebeard/treebeard/tests/jenkins/quality.sh deleted file mode 100644 index db98d4a65..000000000 --- a/wagtail/vendor/django-treebeard/treebeard/tests/jenkins/quality.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/bin/sh - -# Script that takes source code quality measurements. -# In shell because it will run in a *NIX node. - -pip install pylint pep8 pytest coverage -coverage erase - -# Combining the coverage data of all the test runs -# in the different OS/Python combinations. -find $WORKSPACE -mindepth 1 -maxdepth 2 -name '.coverage.*' -exec cp -v \{\} . \; -coverage combine -coverage report -coverage xml -coverage erase - -SRCFILES=treebeard/*.py -PYFILES=`find . -name '*.py'` -ALLFILES=`find . -type f` -sloccount --duplicates --wide --details $ALLFILES > sloccount.sc 2>&1 -pep8 $PYFILES > violations-pep8.txt 2>&1 -pylint -f parseable $SRCFILES > violations-pylint.txt 2>&1 diff --git a/wagtail/vendor/django-treebeard/treebeard/tests/jenkins/rm_workspace_coverage.py b/wagtail/vendor/django-treebeard/treebeard/tests/jenkins/rm_workspace_coverage.py deleted file mode 100644 index d56bb050d..000000000 --- a/wagtail/vendor/django-treebeard/treebeard/tests/jenkins/rm_workspace_coverage.py +++ /dev/null @@ -1,22 +0,0 @@ -"""Remove `.coverage.$HOST.$ID` files from previous runs. - -In Python because of portability with Windows. -""" - -import sys - -import os - - -def main(): - workspace = os.environ['WORKSPACE'] - for filename in os.listdir(workspace): - if filename.startswith('.coverage.'): - file_full_name = os.path.join(workspace, filename) - sys.stdout.write( - '* Removing old .coverage file: `%s`\n' % file_full_name) - os.unlink(file_full_name) - sys.stdout.flush() - -if __name__ == '__main__': - main() diff --git a/wagtail/vendor/django-treebeard/treebeard/tests/jenkins/toxhelper.py b/wagtail/vendor/django-treebeard/treebeard/tests/jenkins/toxhelper.py deleted file mode 100644 index b0a170449..000000000 --- a/wagtail/vendor/django-treebeard/treebeard/tests/jenkins/toxhelper.py +++ /dev/null @@ -1,33 +0,0 @@ -#!/usr/bin/env python -""" toxhelper is a simple wrapper of pytest and coverage to be used with tox. - -It is specially useful to avoid path and interpreter problems while running -tests with jenkins in OS X, Linux and Windows using the same configuration. - -See https://tabo.pe/jenkins/ for the results. -""" - -import sys - -import os -import pytest - -from coverage import coverage - - -def run_the_tests(): - if 'TOX_DB' in os.environ: - os.environ['DATABASE_HOST'], os.environ['DATABASE_PORT'] = { - 'pgsql': ('dummy_test_database_server', '5434'), - 'mysql': ('dummy_test_database_server', '3308'), - 'sqlite': ('', ''), - }[os.environ['TOX_DB']] - cov = coverage() - cov.start() - test_result = pytest.main(sys.argv[1:]) - cov.stop() - cov.save() - return test_result - -if __name__ == '__main__': - sys.exit(run_the_tests()) diff --git a/wagtail/vendor/django-treebeard/treebeard/tests/models.py b/wagtail/vendor/django-treebeard/treebeard/tests/models.py deleted file mode 100644 index 043fe1c4f..000000000 --- a/wagtail/vendor/django-treebeard/treebeard/tests/models.py +++ /dev/null @@ -1,246 +0,0 @@ -from django.db import models, connection -from django.contrib.auth.models import User - -from treebeard.mp_tree import MP_Node -from treebeard.al_tree import AL_Node -from treebeard.ns_tree import NS_Node - - -class RelatedModel(models.Model): - desc = models.CharField(max_length=255) - - def __str__(self): - return self.desc - - -class MP_TestNode(MP_Node): - steplen = 3 - - desc = models.CharField(max_length=255) - - def __str__(self): # pragma: no cover - return 'Node %d' % self.pk - - -class MP_UnicodeNode(MP_Node): - steplen = 3 - - desc = models.CharField(max_length=255) - - def __str__(self): # pragma: no cover - return self.desc - - -class MP_TestNodeSomeDep(models.Model): - node = models.ForeignKey(MP_TestNode) - - def __str__(self): # pragma: no cover - return 'Node %d' % self.pk - - -class MP_TestNodeRelated(MP_Node): - steplen = 3 - - desc = models.CharField(max_length=255) - related = models.ForeignKey(RelatedModel) - - def __str__(self): # pragma: no cover - return 'Node %d' % self.pk - - -class NS_TestNode(NS_Node): - desc = models.CharField(max_length=255) - - def __str__(self): # pragma: no cover - return 'Node %d' % self.pk - - -class NS_UnicodetNode(NS_Node): - desc = models.CharField(max_length=255) - - def __str__(self): # pragma: no cover - return self.desc - - -class NS_TestNodeSomeDep(models.Model): - node = models.ForeignKey(NS_TestNode) - - def __str__(self): # pragma: no cover - return 'Node %d' % self.pk - - -class NS_TestNodeRelated(NS_Node): - desc = models.CharField(max_length=255) - related = models.ForeignKey(RelatedModel) - - def __str__(self): # pragma: no cover - return 'Node %d' % self.pk - - -class AL_TestNode(AL_Node): - parent = models.ForeignKey('self', - related_name='children_set', - null=True, - db_index=True) - sib_order = models.PositiveIntegerField() - desc = models.CharField(max_length=255) - - def __str__(self): # pragma: no cover - return 'Node %d' % self.pk - - -class AL_UnicodeNode(AL_Node): - parent = models.ForeignKey('self', - related_name='children_set', - null=True, - db_index=True) - sib_order = models.PositiveIntegerField() - desc = models.CharField(max_length=255) - - def __str__(self): # pragma: no cover - return self.desc - - -class AL_TestNodeSomeDep(models.Model): - node = models.ForeignKey(AL_TestNode) - - def __str__(self): # pragma: no cover - return 'Node %d' % self.pk - - -class AL_TestNodeRelated(AL_Node): - parent = models.ForeignKey('self', - related_name='children_set', - null=True, - db_index=True) - sib_order = models.PositiveIntegerField() - desc = models.CharField(max_length=255) - related = models.ForeignKey(RelatedModel) - - def __str__(self): # pragma: no cover - return 'Node %d' % self.pk - - -class MP_TestNodeSorted(MP_Node): - steplen = 1 - node_order_by = ['val1', 'val2', 'desc'] - val1 = models.IntegerField() - val2 = models.IntegerField() - desc = models.CharField(max_length=255) - - def __str__(self): # pragma: no cover - return 'Node %d' % self.pk - - -class NS_TestNodeSorted(NS_Node): - node_order_by = ['val1', 'val2', 'desc'] - val1 = models.IntegerField() - val2 = models.IntegerField() - desc = models.CharField(max_length=255) - - def __str__(self): # pragma: no cover - return 'Node %d' % self.pk - - -class AL_TestNodeSorted(AL_Node): - parent = models.ForeignKey('self', - related_name='children_set', - null=True, - db_index=True) - node_order_by = ['val1', 'val2', 'desc'] - val1 = models.IntegerField() - val2 = models.IntegerField() - desc = models.CharField(max_length=255) - - def __str__(self): # pragma: no cover - return 'Node %d' % self.pk - - -class MP_TestNodeAlphabet(MP_Node): - steplen = 2 - - numval = models.IntegerField() - - def __str__(self): # pragma: no cover - return 'Node %d' % self.pk - - -class MP_TestNodeSmallStep(MP_Node): - steplen = 1 - alphabet = '0123456789' - - def __str__(self): # pragma: no cover - return 'Node %d' % self.pk - - -class MP_TestNodeSortedAutoNow(MP_Node): - desc = models.CharField(max_length=255) - created = models.DateTimeField(auto_now_add=True) - - node_order_by = ['created'] - - def __str__(self): # pragma: no cover - return 'Node %d' % self.pk - - -class MP_TestNodeShortPath(MP_Node): - steplen = 1 - alphabet = '01234' - desc = models.CharField(max_length=255) - - def __str__(self): # pragma: no cover - return 'Node %d' % self.pk - - -# This is how you change the default fields defined in a Django abstract class -# (in this case, MP_Node), since Django doesn't allow overriding fields, only -# mehods and attributes -MP_TestNodeShortPath._meta.get_field('path').max_length = 4 - - -class MP_TestNode_Proxy(MP_TestNode): - class Meta: - proxy = True - - -class NS_TestNode_Proxy(NS_TestNode): - class Meta: - proxy = True - - -class AL_TestNode_Proxy(AL_TestNode): - class Meta: - proxy = True - - -class MP_TestSortedNodeShortPath(MP_Node): - steplen = 1 - alphabet = '01234' - desc = models.CharField(max_length=255) - - node_order_by = ['desc'] - - def __str__(self): # pragma: no cover - return 'Node %d' % self.pk - - -MP_TestSortedNodeShortPath._meta.get_field('path').max_length = 4 - - -class MP_TestManyToManyWithUser(MP_Node): - name = models.CharField(max_length=255) - users = models.ManyToManyField(User) - - -BASE_MODELS = AL_TestNode, MP_TestNode, NS_TestNode -PROXY_MODELS = AL_TestNode_Proxy, MP_TestNode_Proxy, NS_TestNode_Proxy -SORTED_MODELS = AL_TestNodeSorted, MP_TestNodeSorted, NS_TestNodeSorted -DEP_MODELS = AL_TestNodeSomeDep, MP_TestNodeSomeDep, NS_TestNodeSomeDep -MP_SHORTPATH_MODELS = MP_TestNodeShortPath, MP_TestSortedNodeShortPath -RELATED_MODELS = AL_TestNodeRelated, MP_TestNodeRelated, NS_TestNodeRelated -UNICODE_MODELS = AL_UnicodeNode, MP_UnicodeNode, NS_UnicodetNode - - -def empty_models_tables(models): - for model in models: - model.objects.all().delete() diff --git a/wagtail/vendor/django-treebeard/treebeard/tests/settings.py b/wagtail/vendor/django-treebeard/treebeard/tests/settings.py deleted file mode 100644 index 5b941136b..000000000 --- a/wagtail/vendor/django-treebeard/treebeard/tests/settings.py +++ /dev/null @@ -1,61 +0,0 @@ -"""Django settings for testing treebeard""" - -import random -import string - -import os - - -def get_db_conf(): - conf, options = {}, {} - for name in ('ENGINE', 'NAME', 'USER', 'PASSWORD', 'HOST', 'PORT'): - conf[name] = os.environ.get('DATABASE_' + name, '') - engine = conf['ENGINE'] - if engine == '': - engine = 'sqlite3' - elif engine in ('pgsql', 'postgres', 'postgresql', 'psycopg2'): - engine = 'postgresql_psycopg2' - if '.' not in engine: - engine = 'django.db.backends.' + engine - conf['ENGINE'] = engine - - if engine == 'django.db.backends.sqlite3': - conf['TEST_NAME'] = conf['NAME'] = ':memory:' - elif engine in ('django.db.backends.mysql', - 'django.db.backends.postgresql_psycopg2'): - if not conf['NAME']: - conf['NAME'] = 'treebeard' - - # randomizing the test db name, - # so we can safely run multiple - # tests at the same time - conf['TEST_NAME'] = "test_%s_%s" % ( - conf['NAME'], - ''.join(random.choice(string.ascii_letters) for _ in range(15)) - ) - - if conf['USER'] == '': - conf['USER'] = { - 'django.db.backends.mysql': 'root', - 'django.db.backends.postgresql_psycopg2': 'postgres' - }[engine] - if engine == 'django.db.backends.mysql': - conf['OPTIONS'] = { - 'init_command': 'SET storage_engine=INNODB,' - 'character_set_connection=utf8,' - 'collation_connection=utf8_unicode_ci'} - return conf - -DATABASES = {'default': get_db_conf()} -SECRET_KEY = '7r33b34rd' - -INSTALLED_APPS = [ - 'django.contrib.auth', - 'django.contrib.contenttypes', - 'django.contrib.sessions', - 'django.contrib.admin', - 'django.contrib.messages', - 'treebeard', - 'treebeard.tests'] - -ROOT_URLCONF = 'treebeard.tests.urls' \ No newline at end of file diff --git a/wagtail/vendor/django-treebeard/treebeard/tests/test_treebeard.py b/wagtail/vendor/django-treebeard/treebeard/tests/test_treebeard.py deleted file mode 100644 index 33ab829e2..000000000 --- a/wagtail/vendor/django-treebeard/treebeard/tests/test_treebeard.py +++ /dev/null @@ -1,2423 +0,0 @@ -# -*- coding: utf-8 -*- -"""Unit/Functional tests""" - -from __future__ import with_statement, unicode_literals -import datetime -import os -import sys - -from django.contrib.admin.sites import AdminSite -from django.contrib.admin.views.main import ChangeList -from django.contrib.auth.models import User -from django.contrib.messages.storage.fallback import FallbackStorage -from django.db.models import Q -from django.template import Template, Context -from django.test import TestCase -from django.test.client import RequestFactory -import pytest - -from treebeard import numconv -from treebeard.admin import admin_factory -from treebeard.exceptions import InvalidPosition, InvalidMoveToDescendant,\ - PathOverflow, MissingNodeOrderBy -from treebeard.forms import movenodeform_factory -from treebeard.templatetags.admin_tree import get_static_url -from treebeard.tests import models - - -BASE_DATA = [ - {'data': {'desc': '1'}}, - {'data': {'desc': '2'}, 'children': [ - {'data': {'desc': '21'}}, - {'data': {'desc': '22'}}, - {'data': {'desc': '23'}, 'children': [ - {'data': {'desc': '231'}}, - ]}, - {'data': {'desc': '24'}}, - ]}, - {'data': {'desc': '3'}}, - {'data': {'desc': '4'}, 'children': [ - {'data': {'desc': '41'}}, - ]}] -UNCHANGED = [ - ('1', 1, 0), - ('2', 1, 4), - ('21', 2, 0), - ('22', 2, 0), - ('23', 2, 1), - ('231', 3, 0), - ('24', 2, 0), - ('3', 1, 0), - ('4', 1, 1), - ('41', 2, 0)] - - -def _prepare_db_test(request): - case = TestCase(methodName='__init__') - case._pre_setup() - request.addfinalizer(case._post_teardown) - return request.param - - -@pytest.fixture(scope='function', - params=models.BASE_MODELS + models.PROXY_MODELS) -def model(request): - return _prepare_db_test(request) - - -@pytest.fixture(scope='function', params=models.BASE_MODELS) -def model_without_proxy(request): - return _prepare_db_test(request) - - -@pytest.fixture(scope='function', params=models.UNICODE_MODELS) -def model_with_unicode(request): - return _prepare_db_test(request) - - -@pytest.fixture(scope='function', params=models.SORTED_MODELS) -def sorted_model(request): - return _prepare_db_test(request) - - -@pytest.fixture(scope='function', params=models.RELATED_MODELS) -def related_model(request): - return _prepare_db_test(request) - - -@pytest.fixture(scope='function', params=models.MP_SHORTPATH_MODELS) -def mpshort_model(request): - return _prepare_db_test(request) - - -@pytest.fixture(scope='function', params=[models.MP_TestNodeShortPath]) -def mpshortnotsorted_model(request): - return _prepare_db_test(request) - - -@pytest.fixture(scope='function', params=[models.MP_TestNodeAlphabet]) -def mpalphabet_model(request): - return _prepare_db_test(request) - - -@pytest.fixture(scope='function', params=[models.MP_TestNodeSortedAutoNow]) -def mpsortedautonow_model(request): - return _prepare_db_test(request) - - -@pytest.fixture(scope='function', params=[models.MP_TestNodeSmallStep]) -def mpsmallstep_model(request): - return _prepare_db_test(request) - - -@pytest.fixture(scope='function', params=[models.MP_TestManyToManyWithUser]) -def mpm2muser_model(request): - return _prepare_db_test(request) - - -class TestTreeBase(object): - def got(self, model): - if model in [models.NS_TestNode, models.NS_TestNode_Proxy]: - # this slows down nested sets tests quite a bit, but it has the - # advantage that we'll check the node edges are correct - d = {} - for tree_id, lft, rgt in model.objects.values_list('tree_id', - 'lft', - 'rgt'): - d.setdefault(tree_id, []).extend([lft, rgt]) - for tree_id, got_edges in d.items(): - assert len(got_edges) == max(got_edges) - good_edges = list(range(1, len(got_edges) + 1)) - assert sorted(got_edges) == good_edges - - return [(o.desc, o.get_depth(), o.get_children_count()) - for o in model.get_tree()] - - def _assert_get_annotated_list(self, model, expected, parent=None): - got = [ - (obj[0].desc, obj[1]['open'], obj[1]['close'], obj[1]['level']) - for obj in model.get_annotated_list(parent) - ] - assert expected == got - - -class TestEmptyTree(TestTreeBase): - - def test_load_bulk_empty(self, model): - ids = model.load_bulk(BASE_DATA) - got_descs = [obj.desc - for obj in model.objects.filter(id__in=ids)] - expected_descs = [x[0] for x in UNCHANGED] - assert sorted(got_descs) == sorted(expected_descs) - assert self.got(model) == UNCHANGED - - def test_dump_bulk_empty(self, model): - assert model.dump_bulk() == [] - - def test_add_root_empty(self, model): - model.add_root(desc='1') - expected = [('1', 1, 0)] - assert self.got(model) == expected - - def test_get_root_nodes_empty(self, model): - got = model.get_root_nodes() - expected = [] - assert [node.desc for node in got] == expected - - def test_get_first_root_node_empty(self, model): - got = model.get_first_root_node() - assert got is None - - def test_get_last_root_node_empty(self, model): - got = model.get_last_root_node() - assert got is None - - def test_get_tree(self, model): - got = list(model.get_tree()) - assert got == [] - - def test_get_annotated_list(self, model): - expected = [] - self._assert_get_annotated_list(model, expected) - - -class TestNonEmptyTree(TestTreeBase): - - @classmethod - def setup_class(cls): - for model in models.BASE_MODELS: - model.load_bulk(BASE_DATA) - - @classmethod - def teardown_class(cls): - models.empty_models_tables(models.BASE_MODELS) - - -class TestClassMethods(TestNonEmptyTree): - - def test_load_bulk_existing(self, model): - # inserting on an existing node - node = model.objects.get(desc='231') - ids = model.load_bulk(BASE_DATA, node) - expected = [('1', 1, 0), - ('2', 1, 4), - ('21', 2, 0), - ('22', 2, 0), - ('23', 2, 1), - ('231', 3, 4), - ('1', 4, 0), - ('2', 4, 4), - ('21', 5, 0), - ('22', 5, 0), - ('23', 5, 1), - ('231', 6, 0), - ('24', 5, 0), - ('3', 4, 0), - ('4', 4, 1), - ('41', 5, 0), - ('24', 2, 0), - ('3', 1, 0), - ('4', 1, 1), - ('41', 2, 0)] - expected_descs = ['1', '2', '21', '22', '23', '231', '24', - '3', '4', '41'] - got_descs = [obj.desc for obj in model.objects.filter(id__in=ids)] - assert sorted(got_descs) == sorted(expected_descs) - assert self.got(model) == expected - - def test_get_tree_all(self, model): - got = [(o.desc, o.get_depth(), o.get_children_count()) - for o in model.get_tree()] - assert got == UNCHANGED - - def test_dump_bulk_all(self, model): - assert model.dump_bulk(keep_ids=False) == BASE_DATA - - def test_get_tree_node(self, model): - node = model.objects.get(desc='231') - model.load_bulk(BASE_DATA, node) - - # the tree was modified by load_bulk, so we reload our node object - node = model.objects.get(pk=node.pk) - - got = [(o.desc, o.get_depth(), o.get_children_count()) - for o in model.get_tree(node)] - expected = [('231', 3, 4), - ('1', 4, 0), - ('2', 4, 4), - ('21', 5, 0), - ('22', 5, 0), - ('23', 5, 1), - ('231', 6, 0), - ('24', 5, 0), - ('3', 4, 0), - ('4', 4, 1), - ('41', 5, 0)] - assert got == expected - - def test_get_tree_leaf(self, model): - node = model.objects.get(desc='1') - - assert 0 == node.get_children_count() - got = [(o.desc, o.get_depth(), o.get_children_count()) - for o in model.get_tree(node)] - expected = [('1', 1, 0)] - assert got == expected - - def test_get_annotated_list_all(self, model): - expected = [('1', True, [], 0), ('2', False, [], 0), - ('21', True, [], 1), ('22', False, [], 1), - ('23', False, [], 1), ('231', True, [0], 2), - ('24', False, [0], 1), ('3', False, [], 0), - ('4', False, [], 0), ('41', True, [0, 1], 1)] - self._assert_get_annotated_list(model, expected) - - def test_get_annotated_list_node(self, model): - node = model.objects.get(desc='2') - expected = [('2', True, [], 0), ('21', True, [], 1), - ('22', False, [], 1), ('23', False, [], 1), - ('231', True, [0], 2), ('24', False, [0, 1], 1)] - self._assert_get_annotated_list(model, expected, node) - - def test_get_annotated_list_leaf(self, model): - node = model.objects.get(desc='1') - expected = [('1', True, [0], 0)] - self._assert_get_annotated_list(model, expected, node) - - def test_dump_bulk_node(self, model): - node = model.objects.get(desc='231') - model.load_bulk(BASE_DATA, node) - - # the tree was modified by load_bulk, so we reload our node object - node = model.objects.get(pk=node.pk) - - got = model.dump_bulk(node, False) - expected = [{'data': {'desc': '231'}, 'children': BASE_DATA}] - assert got == expected - - def test_load_and_dump_bulk_keeping_ids(self, model): - exp = model.dump_bulk(keep_ids=True) - model.objects.all().delete() - model.load_bulk(exp, None, True) - got = model.dump_bulk(keep_ids=True) - assert got == exp - # do we really have an unchaged tree after the dump/delete/load? - got = [(o.desc, o.get_depth(), o.get_children_count()) - for o in model.get_tree()] - assert got == UNCHANGED - - def test_load_and_dump_bulk_with_fk(self, related_model): - # https://bitbucket.org/tabo/django-treebeard/issue/48/ - related_model.objects.all().delete() - related, created = models.RelatedModel.objects.get_or_create( - desc="Test %s" % related_model.__name__) - - related_data = [ - {'data': {'desc': '1', 'related': related.pk}}, - {'data': {'desc': '2', 'related': related.pk}, 'children': [ - {'data': {'desc': '21', 'related': related.pk}}, - {'data': {'desc': '22', 'related': related.pk}}, - {'data': {'desc': '23', 'related': related.pk}, 'children': [ - {'data': {'desc': '231', 'related': related.pk}}, - ]}, - {'data': {'desc': '24', 'related': related.pk}}, - ]}, - {'data': {'desc': '3', 'related': related.pk}}, - {'data': {'desc': '4', 'related': related.pk}, 'children': [ - {'data': {'desc': '41', 'related': related.pk}}, - ]}] - related_model.load_bulk(related_data) - got = related_model.dump_bulk(keep_ids=False) - assert got == related_data - - def test_get_root_nodes(self, model): - got = model.get_root_nodes() - expected = ['1', '2', '3', '4'] - assert [node.desc for node in got] == expected - - def test_get_first_root_node(self, model): - got = model.get_first_root_node() - assert got.desc == '1' - - def test_get_last_root_node(self, model): - got = model.get_last_root_node() - assert got.desc == '4' - - def test_add_root(self, model): - obj = model.add_root(desc='5') - assert obj.get_depth() == 1 - assert model.get_last_root_node().desc == '5' - - -class TestSimpleNodeMethods(TestNonEmptyTree): - def test_is_root(self, model): - data = [ - ('2', True), - ('1', True), - ('4', True), - ('21', False), - ('24', False), - ('22', False), - ('231', False), - ] - for desc, expected in data: - got = model.objects.get(desc=desc).is_root() - assert got == expected - - def test_is_leaf(self, model): - data = [ - ('2', False), - ('23', False), - ('231', True), - ] - for desc, expected in data: - got = model.objects.get(desc=desc).is_leaf() - assert got == expected - - def test_get_root(self, model): - data = [ - ('2', '2'), - ('1', '1'), - ('4', '4'), - ('21', '2'), - ('24', '2'), - ('22', '2'), - ('231', '2'), - ] - for desc, expected in data: - node = model.objects.get(desc=desc).get_root() - assert node.desc == expected - - def test_get_parent(self, model): - data = [ - ('2', None), - ('1', None), - ('4', None), - ('21', '2'), - ('24', '2'), - ('22', '2'), - ('231', '23'), - ] - data = dict(data) - objs = {} - for desc, expected in data.items(): - node = model.objects.get(desc=desc) - parent = node.get_parent() - if expected: - assert parent.desc == expected - else: - assert parent is None - objs[desc] = node - # corrupt the objects' parent cache - node._parent_obj = 'CORRUPTED!!!' - - for desc, expected in data.items(): - node = objs[desc] - # asking get_parent to not use the parent cache (since we - # corrupted it in the previous loop) - parent = node.get_parent(True) - if expected: - assert parent.desc == expected - else: - assert parent is None - - def test_get_children(self, model): - data = [ - ('2', ['21', '22', '23', '24']), - ('23', ['231']), - ('231', []), - ] - for desc, expected in data: - children = model.objects.get(desc=desc).get_children() - assert [node.desc for node in children] == expected - - def test_get_children_count(self, model): - data = [ - ('2', 4), - ('23', 1), - ('231', 0), - ] - for desc, expected in data: - got = model.objects.get(desc=desc).get_children_count() - assert got == expected - - def test_get_siblings(self, model): - data = [ - ('2', ['1', '2', '3', '4']), - ('21', ['21', '22', '23', '24']), - ('231', ['231']), - ] - for desc, expected in data: - siblings = model.objects.get(desc=desc).get_siblings() - assert [node.desc for node in siblings] == expected - - def test_get_first_sibling(self, model): - data = [ - ('2', '1'), - ('1', '1'), - ('4', '1'), - ('21', '21'), - ('24', '21'), - ('22', '21'), - ('231', '231'), - ] - for desc, expected in data: - node = model.objects.get(desc=desc).get_first_sibling() - assert node.desc == expected - - def test_get_prev_sibling(self, model): - data = [ - ('2', '1'), - ('1', None), - ('4', '3'), - ('21', None), - ('24', '23'), - ('22', '21'), - ('231', None), - ] - for desc, expected in data: - node = model.objects.get(desc=desc).get_prev_sibling() - if expected is None: - assert node is None - else: - assert node.desc == expected - - def test_get_next_sibling(self, model): - data = [ - ('2', '3'), - ('1', '2'), - ('4', None), - ('21', '22'), - ('24', None), - ('22', '23'), - ('231', None), - ] - for desc, expected in data: - node = model.objects.get(desc=desc).get_next_sibling() - if expected is None: - assert node is None - else: - assert node.desc == expected - - def test_get_last_sibling(self, model): - data = [ - ('2', '4'), - ('1', '4'), - ('4', '4'), - ('21', '24'), - ('24', '24'), - ('22', '24'), - ('231', '231'), - ] - for desc, expected in data: - node = model.objects.get(desc=desc).get_last_sibling() - assert node.desc == expected - - def test_get_first_child(self, model): - data = [ - ('2', '21'), - ('21', None), - ('23', '231'), - ('231', None), - ] - for desc, expected in data: - node = model.objects.get(desc=desc).get_first_child() - if expected is None: - assert node is None - else: - assert node.desc == expected - - def test_get_last_child(self, model): - data = [ - ('2', '24'), - ('21', None), - ('23', '231'), - ('231', None), - ] - for desc, expected in data: - node = model.objects.get(desc=desc).get_last_child() - if expected is None: - assert node is None - else: - assert node.desc == expected - - def test_get_ancestors(self, model): - data = [ - ('2', []), - ('21', ['2']), - ('231', ['2', '23']), - ] - for desc, expected in data: - nodes = model.objects.get(desc=desc).get_ancestors() - assert [node.desc for node in nodes] == expected - - def test_get_descendants(self, model): - data = [ - ('2', ['21', '22', '23', '231', '24']), - ('23', ['231']), - ('231', []), - ('1', []), - ('4', ['41']), - ] - for desc, expected in data: - nodes = model.objects.get(desc=desc).get_descendants() - assert [node.desc for node in nodes] == expected - - def test_get_descendant_count(self, model): - data = [ - ('2', 5), - ('23', 1), - ('231', 0), - ('1', 0), - ('4', 1), - ] - for desc, expected in data: - got = model.objects.get(desc=desc).get_descendant_count() - assert got == expected - - def test_is_sibling_of(self, model): - data = [ - ('2', '2', True), - ('2', '1', True), - ('21', '2', False), - ('231', '2', False), - ('22', '23', True), - ('231', '23', False), - ('231', '231', True), - ] - for desc1, desc2, expected in data: - node1 = model.objects.get(desc=desc1) - node2 = model.objects.get(desc=desc2) - assert node1.is_sibling_of(node2) == expected - - def test_is_child_of(self, model): - data = [ - ('2', '2', False), - ('2', '1', False), - ('21', '2', True), - ('231', '2', False), - ('231', '23', True), - ('231', '231', False), - ] - for desc1, desc2, expected in data: - node1 = model.objects.get(desc=desc1) - node2 = model.objects.get(desc=desc2) - assert node1.is_child_of(node2) == expected - - def test_is_descendant_of(self, model): - data = [ - ('2', '2', False), - ('2', '1', False), - ('21', '2', True), - ('231', '2', True), - ('231', '23', True), - ('231', '231', False), - ] - for desc1, desc2, expected in data: - node1 = model.objects.get(desc=desc1) - node2 = model.objects.get(desc=desc2) - assert node1.is_descendant_of(node2) == expected - - -class TestAddChild(TestNonEmptyTree): - def test_add_child_to_leaf(self, model): - model.objects.get(desc='231').add_child(desc='2311') - expected = [('1', 1, 0), - ('2', 1, 4), - ('21', 2, 0), - ('22', 2, 0), - ('23', 2, 1), - ('231', 3, 1), - ('2311', 4, 0), - ('24', 2, 0), - ('3', 1, 0), - ('4', 1, 1), - ('41', 2, 0)] - assert self.got(model) == expected - - def test_add_child_to_node(self, model): - model.objects.get(desc='2').add_child(desc='25') - expected = [('1', 1, 0), - ('2', 1, 5), - ('21', 2, 0), - ('22', 2, 0), - ('23', 2, 1), - ('231', 3, 0), - ('24', 2, 0), - ('25', 2, 0), - ('3', 1, 0), - ('4', 1, 1), - ('41', 2, 0)] - assert self.got(model) == expected - - -class TestAddSibling(TestNonEmptyTree): - def test_add_sibling_invalid_pos(self, model): - with pytest.raises(InvalidPosition): - model.objects.get(desc='231').add_sibling('invalid_pos') - - def test_add_sibling_missing_nodeorderby(self, model): - node_wchildren = model.objects.get(desc='2') - with pytest.raises(MissingNodeOrderBy): - node_wchildren.add_sibling('sorted-sibling', desc='aaa') - - def test_add_sibling_last_root(self, model): - node_wchildren = model.objects.get(desc='2') - obj = node_wchildren.add_sibling('last-sibling', desc='5') - assert obj.get_depth() == 1 - assert node_wchildren.get_last_sibling().desc == '5' - - def test_add_sibling_last(self, model): - node = model.objects.get(desc='231') - obj = node.add_sibling('last-sibling', desc='232') - assert obj.get_depth() == 3 - assert node.get_last_sibling().desc == '232' - - def test_add_sibling_first_root(self, model): - node_wchildren = model.objects.get(desc='2') - obj = node_wchildren.add_sibling('first-sibling', desc='new') - assert obj.get_depth() == 1 - expected = [('new', 1, 0), - ('1', 1, 0), - ('2', 1, 4), - ('21', 2, 0), - ('22', 2, 0), - ('23', 2, 1), - ('231', 3, 0), - ('24', 2, 0), - ('3', 1, 0), - ('4', 1, 1), - ('41', 2, 0)] - assert self.got(model) == expected - - def test_add_sibling_first(self, model): - node_wchildren = model.objects.get(desc='23') - obj = node_wchildren.add_sibling('first-sibling', desc='new') - assert obj.get_depth() == 2 - expected = [('1', 1, 0), - ('2', 1, 5), - ('new', 2, 0), - ('21', 2, 0), - ('22', 2, 0), - ('23', 2, 1), - ('231', 3, 0), - ('24', 2, 0), - ('3', 1, 0), - ('4', 1, 1), - ('41', 2, 0)] - assert self.got(model) == expected - - def test_add_sibling_left_root(self, model): - node_wchildren = model.objects.get(desc='2') - obj = node_wchildren.add_sibling('left', desc='new') - assert obj.get_depth() == 1 - expected = [('1', 1, 0), - ('new', 1, 0), - ('2', 1, 4), - ('21', 2, 0), - ('22', 2, 0), - ('23', 2, 1), - ('231', 3, 0), - ('24', 2, 0), - ('3', 1, 0), - ('4', 1, 1), - ('41', 2, 0)] - assert self.got(model) == expected - - def test_add_sibling_left(self, model): - node_wchildren = model.objects.get(desc='23') - obj = node_wchildren.add_sibling('left', desc='new') - assert obj.get_depth() == 2 - expected = [('1', 1, 0), - ('2', 1, 5), - ('21', 2, 0), - ('22', 2, 0), - ('new', 2, 0), - ('23', 2, 1), - ('231', 3, 0), - ('24', 2, 0), - ('3', 1, 0), - ('4', 1, 1), - ('41', 2, 0)] - assert self.got(model) == expected - - def test_add_sibling_left_noleft_root(self, model): - node = model.objects.get(desc='1') - obj = node.add_sibling('left', desc='new') - assert obj.get_depth() == 1 - expected = [('new', 1, 0), - ('1', 1, 0), - ('2', 1, 4), - ('21', 2, 0), - ('22', 2, 0), - ('23', 2, 1), - ('231', 3, 0), - ('24', 2, 0), - ('3', 1, 0), - ('4', 1, 1), - ('41', 2, 0)] - assert self.got(model) == expected - - def test_add_sibling_left_noleft(self, model): - node = model.objects.get(desc='231') - obj = node.add_sibling('left', desc='new') - assert obj.get_depth() == 3 - expected = [('1', 1, 0), - ('2', 1, 4), - ('21', 2, 0), - ('22', 2, 0), - ('23', 2, 2), - ('new', 3, 0), - ('231', 3, 0), - ('24', 2, 0), - ('3', 1, 0), - ('4', 1, 1), - ('41', 2, 0)] - assert self.got(model) == expected - - def test_add_sibling_right_root(self, model): - node_wchildren = model.objects.get(desc='2') - obj = node_wchildren.add_sibling('right', desc='new') - assert obj.get_depth() == 1 - expected = [('1', 1, 0), - ('2', 1, 4), - ('21', 2, 0), - ('22', 2, 0), - ('23', 2, 1), - ('231', 3, 0), - ('24', 2, 0), - ('new', 1, 0), - ('3', 1, 0), - ('4', 1, 1), - ('41', 2, 0)] - assert self.got(model) == expected - - def test_add_sibling_right(self, model): - node_wchildren = model.objects.get(desc='23') - obj = node_wchildren.add_sibling('right', desc='new') - assert obj.get_depth() == 2 - expected = [('1', 1, 0), - ('2', 1, 5), - ('21', 2, 0), - ('22', 2, 0), - ('23', 2, 1), - ('231', 3, 0), - ('new', 2, 0), - ('24', 2, 0), - ('3', 1, 0), - ('4', 1, 1), - ('41', 2, 0)] - assert self.got(model) == expected - - def test_add_sibling_right_noright_root(self, model): - node = model.objects.get(desc='4') - obj = node.add_sibling('right', desc='new') - assert obj.get_depth() == 1 - expected = [('1', 1, 0), - ('2', 1, 4), - ('21', 2, 0), - ('22', 2, 0), - ('23', 2, 1), - ('231', 3, 0), - ('24', 2, 0), - ('3', 1, 0), - ('4', 1, 1), - ('41', 2, 0), - ('new', 1, 0)] - assert self.got(model) == expected - - def test_add_sibling_right_noright(self, model): - node = model.objects.get(desc='231') - obj = node.add_sibling('right', desc='new') - assert obj.get_depth() == 3 - expected = [('1', 1, 0), - ('2', 1, 4), - ('21', 2, 0), - ('22', 2, 0), - ('23', 2, 2), - ('231', 3, 0), - ('new', 3, 0), - ('24', 2, 0), - ('3', 1, 0), - ('4', 1, 1), - ('41', 2, 0)] - assert self.got(model) == expected - - -class TestDelete(TestNonEmptyTree): - - @classmethod - def setup_class(cls): - TestNonEmptyTree.setup_class() - for model, dep_model in zip(models.BASE_MODELS, models.DEP_MODELS): - for node in model.objects.all(): - dep_model(node=node).save() - - @classmethod - def teardown_class(cls): - models.empty_models_tables(models.DEP_MODELS + models.BASE_MODELS) - - def test_delete_leaf(self, model): - model.objects.get(desc='231').delete() - expected = [('1', 1, 0), - ('2', 1, 4), - ('21', 2, 0), - ('22', 2, 0), - ('23', 2, 0), - ('24', 2, 0), - ('3', 1, 0), - ('4', 1, 1), - ('41', 2, 0)] - assert self.got(model) == expected - - def test_delete_node(self, model): - model.objects.get(desc='23').delete() - expected = [('1', 1, 0), - ('2', 1, 3), - ('21', 2, 0), - ('22', 2, 0), - ('24', 2, 0), - ('3', 1, 0), - ('4', 1, 1), - ('41', 2, 0)] - assert self.got(model) == expected - - def test_delete_root(self, model): - model.objects.get(desc='2').delete() - expected = [('1', 1, 0), - ('3', 1, 0), - ('4', 1, 1), - ('41', 2, 0)] - assert self.got(model) == expected - - def test_delete_filter_root_nodes(self, model): - model.objects.filter(desc__in=('2', '3')).delete() - expected = [('1', 1, 0), - ('4', 1, 1), - ('41', 2, 0)] - assert self.got(model) == expected - - def test_delete_filter_children(self, model): - model.objects.filter(desc__in=('2', '23', '231')).delete() - expected = [('1', 1, 0), - ('3', 1, 0), - ('4', 1, 1), - ('41', 2, 0)] - assert self.got(model) == expected - - def test_delete_nonexistant_nodes(self, model): - model.objects.filter(desc__in=('ZZZ', 'XXX')).delete() - assert self.got(model) == UNCHANGED - - def test_delete_same_node_twice(self, model): - model.objects.filter(desc__in=('2', '2')).delete() - expected = [('1', 1, 0), - ('3', 1, 0), - ('4', 1, 1), - ('41', 2, 0)] - assert self.got(model) == expected - - def test_delete_all_root_nodes(self, model): - model.get_root_nodes().delete() - count = model.objects.count() - assert count == 0 - - def test_delete_all_nodes(self, model): - model.objects.all().delete() - count = model.objects.count() - assert count == 0 - - -class TestMoveErrors(TestNonEmptyTree): - def test_move_invalid_pos(self, model): - node = model.objects.get(desc='231') - with pytest.raises(InvalidPosition): - node.move(node, 'invalid_pos') - - def test_move_to_descendant(self, model): - node = model.objects.get(desc='2') - target = model.objects.get(desc='231') - with pytest.raises(InvalidMoveToDescendant): - node.move(target, 'first-sibling') - - def test_move_missing_nodeorderby(self, model): - node = model.objects.get(desc='231') - with pytest.raises(MissingNodeOrderBy): - node.move(node, 'sorted-child') - with pytest.raises(MissingNodeOrderBy): - node.move(node, 'sorted-sibling') - - -class TestMoveSortedErrors(TestTreeBase): - - def test_nonsorted_move_in_sorted(self, sorted_model): - node = sorted_model.add_root(val1=3, val2=3, desc='zxy') - with pytest.raises(InvalidPosition): - node.move(node, 'left') - - -class TestMoveLeafRoot(TestNonEmptyTree): - def test_move_leaf_last_sibling_root(self, model): - target = model.objects.get(desc='2') - model.objects.get(desc='231').move(target, 'last-sibling') - expected = [('1', 1, 0), - ('2', 1, 4), - ('21', 2, 0), - ('22', 2, 0), - ('23', 2, 0), - ('24', 2, 0), - ('3', 1, 0), - ('4', 1, 1), - ('41', 2, 0), - ('231', 1, 0)] - assert self.got(model) == expected - - def test_move_leaf_first_sibling_root(self, model): - target = model.objects.get(desc='2') - model.objects.get(desc='231').move(target, 'first-sibling') - expected = [('231', 1, 0), - ('1', 1, 0), - ('2', 1, 4), - ('21', 2, 0), - ('22', 2, 0), - ('23', 2, 0), - ('24', 2, 0), - ('3', 1, 0), - ('4', 1, 1), - ('41', 2, 0)] - assert self.got(model) == expected - - def test_move_leaf_left_sibling_root(self, model): - target = model.objects.get(desc='2') - model.objects.get(desc='231').move(target, 'left') - expected = [('1', 1, 0), - ('231', 1, 0), - ('2', 1, 4), - ('21', 2, 0), - ('22', 2, 0), - ('23', 2, 0), - ('24', 2, 0), - ('3', 1, 0), - ('4', 1, 1), - ('41', 2, 0)] - assert self.got(model) == expected - - def test_move_leaf_right_sibling_root(self, model): - target = model.objects.get(desc='2') - model.objects.get(desc='231').move(target, 'right') - expected = [('1', 1, 0), - ('2', 1, 4), - ('21', 2, 0), - ('22', 2, 0), - ('23', 2, 0), - ('24', 2, 0), - ('231', 1, 0), - ('3', 1, 0), - ('4', 1, 1), - ('41', 2, 0)] - assert self.got(model) == expected - - def test_move_leaf_last_child_root(self, model): - target = model.objects.get(desc='2') - model.objects.get(desc='231').move(target, 'last-child') - expected = [('1', 1, 0), - ('2', 1, 5), - ('21', 2, 0), - ('22', 2, 0), - ('23', 2, 0), - ('24', 2, 0), - ('231', 2, 0), - ('3', 1, 0), - ('4', 1, 1), - ('41', 2, 0)] - assert self.got(model) == expected - - def test_move_leaf_first_child_root(self, model): - target = model.objects.get(desc='2') - model.objects.get(desc='231').move(target, 'first-child') - expected = [('1', 1, 0), - ('2', 1, 5), - ('231', 2, 0), - ('21', 2, 0), - ('22', 2, 0), - ('23', 2, 0), - ('24', 2, 0), - ('3', 1, 0), - ('4', 1, 1), - ('41', 2, 0)] - assert self.got(model) == expected - - -class TestMoveLeaf(TestNonEmptyTree): - def test_move_leaf_last_sibling(self, model): - target = model.objects.get(desc='22') - model.objects.get(desc='231').move(target, 'last-sibling') - expected = [('1', 1, 0), - ('2', 1, 5), - ('21', 2, 0), - ('22', 2, 0), - ('23', 2, 0), - ('24', 2, 0), - ('231', 2, 0), - ('3', 1, 0), - ('4', 1, 1), - ('41', 2, 0)] - assert self.got(model) == expected - - def test_move_leaf_first_sibling(self, model): - target = model.objects.get(desc='22') - model.objects.get(desc='231').move(target, 'first-sibling') - expected = [('1', 1, 0), - ('2', 1, 5), - ('231', 2, 0), - ('21', 2, 0), - ('22', 2, 0), - ('23', 2, 0), - ('24', 2, 0), - ('3', 1, 0), - ('4', 1, 1), - ('41', 2, 0)] - assert self.got(model) == expected - - def test_move_leaf_left_sibling(self, model): - target = model.objects.get(desc='22') - model.objects.get(desc='231').move(target, 'left') - expected = [('1', 1, 0), - ('2', 1, 5), - ('21', 2, 0), - ('231', 2, 0), - ('22', 2, 0), - ('23', 2, 0), - ('24', 2, 0), - ('3', 1, 0), - ('4', 1, 1), - ('41', 2, 0)] - assert self.got(model) == expected - - def test_move_leaf_right_sibling(self, model): - target = model.objects.get(desc='22') - model.objects.get(desc='231').move(target, 'right') - expected = [('1', 1, 0), - ('2', 1, 5), - ('21', 2, 0), - ('22', 2, 0), - ('231', 2, 0), - ('23', 2, 0), - ('24', 2, 0), - ('3', 1, 0), - ('4', 1, 1), - ('41', 2, 0)] - assert self.got(model) == expected - - def test_move_leaf_left_sibling_itself(self, model): - target = model.objects.get(desc='231') - model.objects.get(desc='231').move(target, 'left') - assert self.got(model) == UNCHANGED - - def test_move_leaf_last_child(self, model): - target = model.objects.get(desc='22') - model.objects.get(desc='231').move(target, 'last-child') - expected = [('1', 1, 0), - ('2', 1, 4), - ('21', 2, 0), - ('22', 2, 1), - ('231', 3, 0), - ('23', 2, 0), - ('24', 2, 0), - ('3', 1, 0), - ('4', 1, 1), - ('41', 2, 0)] - assert self.got(model) == expected - - def test_move_leaf_first_child(self, model): - target = model.objects.get(desc='22') - model.objects.get(desc='231').move(target, 'first-child') - expected = [('1', 1, 0), - ('2', 1, 4), - ('21', 2, 0), - ('22', 2, 1), - ('231', 3, 0), - ('23', 2, 0), - ('24', 2, 0), - ('3', 1, 0), - ('4', 1, 1), - ('41', 2, 0)] - assert self.got(model) == expected - - -class TestMoveBranchRoot(TestNonEmptyTree): - def test_move_branch_first_sibling_root(self, model): - target = model.objects.get(desc='2') - model.objects.get(desc='4').move(target, 'first-sibling') - expected = [('4', 1, 1), - ('41', 2, 0), - ('1', 1, 0), - ('2', 1, 4), - ('21', 2, 0), - ('22', 2, 0), - ('23', 2, 1), - ('231', 3, 0), - ('24', 2, 0), - ('3', 1, 0)] - assert self.got(model) == expected - - def test_move_branch_last_sibling_root(self, model): - target = model.objects.get(desc='2') - model.objects.get(desc='4').move(target, 'last-sibling') - expected = [('1', 1, 0), - ('2', 1, 4), - ('21', 2, 0), - ('22', 2, 0), - ('23', 2, 1), - ('231', 3, 0), - ('24', 2, 0), - ('3', 1, 0), - ('4', 1, 1), - ('41', 2, 0)] - assert self.got(model) == expected - - def test_move_branch_left_sibling_root(self, model): - target = model.objects.get(desc='2') - model.objects.get(desc='4').move(target, 'left') - expected = [('1', 1, 0), - ('4', 1, 1), - ('41', 2, 0), - ('2', 1, 4), - ('21', 2, 0), - ('22', 2, 0), - ('23', 2, 1), - ('231', 3, 0), - ('24', 2, 0), - ('3', 1, 0)] - assert self.got(model) == expected - - def test_move_branch_right_sibling_root(self, model): - target = model.objects.get(desc='2') - model.objects.get(desc='4').move(target, 'right') - expected = [('1', 1, 0), - ('2', 1, 4), - ('21', 2, 0), - ('22', 2, 0), - ('23', 2, 1), - ('231', 3, 0), - ('24', 2, 0), - ('4', 1, 1), - ('41', 2, 0), - ('3', 1, 0)] - assert self.got(model) == expected - - def test_move_branch_left_noleft_sibling_root(self, model): - target = model.objects.get(desc='2').get_first_sibling() - model.objects.get(desc='4').move(target, 'left') - expected = [('4', 1, 1), - ('41', 2, 0), - ('1', 1, 0), - ('2', 1, 4), - ('21', 2, 0), - ('22', 2, 0), - ('23', 2, 1), - ('231', 3, 0), - ('24', 2, 0), - ('3', 1, 0)] - assert self.got(model) == expected - - def test_move_branch_right_noright_sibling_root(self, model): - target = model.objects.get(desc='2').get_last_sibling() - model.objects.get(desc='4').move(target, 'right') - expected = [('1', 1, 0), - ('2', 1, 4), - ('21', 2, 0), - ('22', 2, 0), - ('23', 2, 1), - ('231', 3, 0), - ('24', 2, 0), - ('3', 1, 0), - ('4', 1, 1), - ('41', 2, 0)] - assert self.got(model) == expected - - def test_move_branch_first_child_root(self, model): - target = model.objects.get(desc='2') - model.objects.get(desc='4').move(target, 'first-child') - expected = [('1', 1, 0), - ('2', 1, 5), - ('4', 2, 1), - ('41', 3, 0), - ('21', 2, 0), - ('22', 2, 0), - ('23', 2, 1), - ('231', 3, 0), - ('24', 2, 0), - ('3', 1, 0)] - assert self.got(model) == expected - - def test_move_branch_last_child_root(self, model): - target = model.objects.get(desc='2') - model.objects.get(desc='4').move(target, 'last-child') - expected = [('1', 1, 0), - ('2', 1, 5), - ('21', 2, 0), - ('22', 2, 0), - ('23', 2, 1), - ('231', 3, 0), - ('24', 2, 0), - ('4', 2, 1), - ('41', 3, 0), - ('3', 1, 0)] - assert self.got(model) == expected - - -class TestMoveBranch(TestNonEmptyTree): - def test_move_branch_first_sibling(self, model): - target = model.objects.get(desc='23') - model.objects.get(desc='4').move(target, 'first-sibling') - expected = [('1', 1, 0), - ('2', 1, 5), - ('4', 2, 1), - ('41', 3, 0), - ('21', 2, 0), - ('22', 2, 0), - ('23', 2, 1), - ('231', 3, 0), - ('24', 2, 0), - ('3', 1, 0)] - assert self.got(model) == expected - - def test_move_branch_last_sibling(self, model): - target = model.objects.get(desc='23') - model.objects.get(desc='4').move(target, 'last-sibling') - expected = [('1', 1, 0), - ('2', 1, 5), - ('21', 2, 0), - ('22', 2, 0), - ('23', 2, 1), - ('231', 3, 0), - ('24', 2, 0), - ('4', 2, 1), - ('41', 3, 0), - ('3', 1, 0)] - assert self.got(model) == expected - - def test_move_branch_left_sibling(self, model): - target = model.objects.get(desc='23') - model.objects.get(desc='4').move(target, 'left') - expected = [('1', 1, 0), - ('2', 1, 5), - ('21', 2, 0), - ('22', 2, 0), - ('4', 2, 1), - ('41', 3, 0), - ('23', 2, 1), - ('231', 3, 0), - ('24', 2, 0), - ('3', 1, 0)] - assert self.got(model) == expected - - def test_move_branch_right_sibling(self, model): - target = model.objects.get(desc='23') - model.objects.get(desc='4').move(target, 'right') - expected = [('1', 1, 0), - ('2', 1, 5), - ('21', 2, 0), - ('22', 2, 0), - ('23', 2, 1), - ('231', 3, 0), - ('4', 2, 1), - ('41', 3, 0), - ('24', 2, 0), - ('3', 1, 0)] - assert self.got(model) == expected - - def test_move_branch_left_noleft_sibling(self, model): - target = model.objects.get(desc='23').get_first_sibling() - model.objects.get(desc='4').move(target, 'left') - expected = [('1', 1, 0), - ('2', 1, 5), - ('4', 2, 1), - ('41', 3, 0), - ('21', 2, 0), - ('22', 2, 0), - ('23', 2, 1), - ('231', 3, 0), - ('24', 2, 0), - ('3', 1, 0)] - assert self.got(model) == expected - - def test_move_branch_right_noright_sibling(self, model): - target = model.objects.get(desc='23').get_last_sibling() - model.objects.get(desc='4').move(target, 'right') - expected = [('1', 1, 0), - ('2', 1, 5), - ('21', 2, 0), - ('22', 2, 0), - ('23', 2, 1), - ('231', 3, 0), - ('24', 2, 0), - ('4', 2, 1), - ('41', 3, 0), - ('3', 1, 0)] - assert self.got(model) == expected - - def test_move_branch_left_itself_sibling(self, model): - target = model.objects.get(desc='4') - model.objects.get(desc='4').move(target, 'left') - assert self.got(model) == UNCHANGED - - def test_move_branch_first_child(self, model): - target = model.objects.get(desc='23') - model.objects.get(desc='4').move(target, 'first-child') - expected = [('1', 1, 0), - ('2', 1, 4), - ('21', 2, 0), - ('22', 2, 0), - ('23', 2, 2), - ('4', 3, 1), - ('41', 4, 0), - ('231', 3, 0), - ('24', 2, 0), - ('3', 1, 0)] - assert self.got(model) == expected - - def test_move_branch_last_child(self, model): - target = model.objects.get(desc='23') - model.objects.get(desc='4').move(target, 'last-child') - expected = [('1', 1, 0), - ('2', 1, 4), - ('21', 2, 0), - ('22', 2, 0), - ('23', 2, 2), - ('231', 3, 0), - ('4', 3, 1), - ('41', 4, 0), - ('24', 2, 0), - ('3', 1, 0)] - assert self.got(model) == expected - - -class TestTreeSorted(TestTreeBase): - - def got(self, sorted_model): - return [(o.val1, o.val2, o.desc, o.get_depth(), o.get_children_count()) - for o in sorted_model.get_tree()] - - def test_add_root_sorted(self, sorted_model): - sorted_model.add_root(val1=3, val2=3, desc='zxy') - sorted_model.add_root(val1=1, val2=4, desc='bcd') - sorted_model.add_root(val1=2, val2=5, desc='zxy') - sorted_model.add_root(val1=3, val2=3, desc='abc') - sorted_model.add_root(val1=4, val2=1, desc='fgh') - sorted_model.add_root(val1=3, val2=3, desc='abc') - sorted_model.add_root(val1=2, val2=2, desc='qwe') - sorted_model.add_root(val1=3, val2=2, desc='vcx') - expected = [(1, 4, 'bcd', 1, 0), - (2, 2, 'qwe', 1, 0), - (2, 5, 'zxy', 1, 0), - (3, 2, 'vcx', 1, 0), - (3, 3, 'abc', 1, 0), - (3, 3, 'abc', 1, 0), - (3, 3, 'zxy', 1, 0), - (4, 1, 'fgh', 1, 0)] - assert self.got(sorted_model) == expected - - def test_add_child_root_sorted(self, sorted_model): - root = sorted_model.add_root(val1=0, val2=0, desc='aaa') - root.add_child(val1=3, val2=3, desc='zxy') - root.add_child(val1=1, val2=4, desc='bcd') - root.add_child(val1=2, val2=5, desc='zxy') - root.add_child(val1=3, val2=3, desc='abc') - root.add_child(val1=4, val2=1, desc='fgh') - root.add_child(val1=3, val2=3, desc='abc') - root.add_child(val1=2, val2=2, desc='qwe') - root.add_child(val1=3, val2=2, desc='vcx') - expected = [(0, 0, 'aaa', 1, 8), - (1, 4, 'bcd', 2, 0), - (2, 2, 'qwe', 2, 0), - (2, 5, 'zxy', 2, 0), - (3, 2, 'vcx', 2, 0), - (3, 3, 'abc', 2, 0), - (3, 3, 'abc', 2, 0), - (3, 3, 'zxy', 2, 0), - (4, 1, 'fgh', 2, 0)] - assert self.got(sorted_model) == expected - - def test_add_child_nonroot_sorted(self, sorted_model): - get_node = lambda node_id: sorted_model.objects.get(pk=node_id) - - root_id = sorted_model.add_root(val1=0, val2=0, desc='a').pk - node_id = get_node(root_id).add_child(val1=0, val2=0, desc='ac').pk - get_node(root_id).add_child(val1=0, val2=0, desc='aa') - get_node(root_id).add_child(val1=0, val2=0, desc='av') - get_node(node_id).add_child(val1=0, val2=0, desc='aca') - get_node(node_id).add_child(val1=0, val2=0, desc='acc') - get_node(node_id).add_child(val1=0, val2=0, desc='acb') - - expected = [(0, 0, 'a', 1, 3), - (0, 0, 'aa', 2, 0), - (0, 0, 'ac', 2, 3), - (0, 0, 'aca', 3, 0), - (0, 0, 'acb', 3, 0), - (0, 0, 'acc', 3, 0), - (0, 0, 'av', 2, 0)] - assert self.got(sorted_model) == expected - - def test_move_sorted(self, sorted_model): - sorted_model.add_root(val1=3, val2=3, desc='zxy') - sorted_model.add_root(val1=1, val2=4, desc='bcd') - sorted_model.add_root(val1=2, val2=5, desc='zxy') - sorted_model.add_root(val1=3, val2=3, desc='abc') - sorted_model.add_root(val1=4, val2=1, desc='fgh') - sorted_model.add_root(val1=3, val2=3, desc='abc') - sorted_model.add_root(val1=2, val2=2, desc='qwe') - sorted_model.add_root(val1=3, val2=2, desc='vcx') - root_nodes = sorted_model.get_root_nodes() - target = root_nodes[0] - for node in root_nodes[1:]: - # because raw queries don't update django objects - node = sorted_model.objects.get(pk=node.pk) - target = sorted_model.objects.get(pk=target.pk) - node.move(target, 'sorted-child') - expected = [(1, 4, 'bcd', 1, 7), - (2, 2, 'qwe', 2, 0), - (2, 5, 'zxy', 2, 0), - (3, 2, 'vcx', 2, 0), - (3, 3, 'abc', 2, 0), - (3, 3, 'abc', 2, 0), - (3, 3, 'zxy', 2, 0), - (4, 1, 'fgh', 2, 0)] - assert self.got(sorted_model) == expected - - def test_move_sortedsibling(self, sorted_model): - # https://bitbucket.org/tabo/django-treebeard/issue/27 - sorted_model.add_root(val1=3, val2=3, desc='zxy') - sorted_model.add_root(val1=1, val2=4, desc='bcd') - sorted_model.add_root(val1=2, val2=5, desc='zxy') - sorted_model.add_root(val1=3, val2=3, desc='abc') - sorted_model.add_root(val1=4, val2=1, desc='fgh') - sorted_model.add_root(val1=3, val2=3, desc='abc') - sorted_model.add_root(val1=2, val2=2, desc='qwe') - sorted_model.add_root(val1=3, val2=2, desc='vcx') - root_nodes = sorted_model.get_root_nodes() - target = root_nodes[0] - for node in root_nodes[1:]: - # because raw queries don't update django objects - node = sorted_model.objects.get(pk=node.pk) - target = sorted_model.objects.get(pk=target.pk) - node.val1 -= 2 - node.save() - node.move(target, 'sorted-sibling') - expected = [(0, 2, 'qwe', 1, 0), - (0, 5, 'zxy', 1, 0), - (1, 2, 'vcx', 1, 0), - (1, 3, 'abc', 1, 0), - (1, 3, 'abc', 1, 0), - (1, 3, 'zxy', 1, 0), - (1, 4, 'bcd', 1, 0), - (2, 1, 'fgh', 1, 0)] - assert self.got(sorted_model) == expected - - -class TestMP_TreeAlphabet(TestTreeBase): - def test_alphabet(self, mpalphabet_model): - if not os.getenv('TREEBEARD_TEST_ALPHABET', False): - # run this test only if the enviroment variable is set - return - basealpha = numconv.BASE85 - got_err = False - last_good = None - for alphabetlen in range(35, len(basealpha) + 1): - alphabet = basealpha[0:alphabetlen] - expected = [alphabet[0] + char for char in alphabet[1:]] - expected.extend([alphabet[1] + char for char in alphabet]) - expected.append(alphabet[2] + alphabet[0]) - - # remove all nodes - mpalphabet_model.objects.all().delete() - - # change the model's alphabet - mpalphabet_model.alphabet = alphabet - - # insert root nodes - for pos in range(len(alphabet) * 2): - try: - mpalphabet_model.add_root(numval=pos) - except: - got_err = True - break - if got_err: - break - got = [obj.path - for obj in mpalphabet_model.objects.all()] - if got != expected: - got_err = True - last_good = alphabet - sys.stdout.write( - '\nThe best BASE85 based alphabet for your setup is: %s\n' % ( - last_good, ) - ) - sys.stdout.flush() - - -class TestHelpers(TestTreeBase): - - @classmethod - def setup_class(cls): - for model in models.BASE_MODELS: - model.load_bulk(BASE_DATA) - for node in model.get_root_nodes(): - model.load_bulk(BASE_DATA, node) - model.add_root(desc='5') - - @classmethod - def teardown_class(cls): - models.empty_models_tables(models.BASE_MODELS) - - def test_descendants_group_count_root(self, model): - expected = [(o.desc, o.get_descendant_count()) - for o in model.get_root_nodes()] - got = [(o.desc, o.descendants_count) - for o in model.get_descendants_group_count()] - assert got == expected - - def test_descendants_group_count_node(self, model): - parent = model.get_root_nodes().get(desc='2') - expected = [(o.desc, o.get_descendant_count()) - for o in parent.get_children()] - got = [(o.desc, o.descendants_count) - for o in model.get_descendants_group_count(parent)] - assert got == expected - - -class TestMP_TreeSortedAutoNow(TestTreeBase): - """ - The sorting mechanism used by treebeard when adding a node can fail if the - ordering is using an "auto_now" field - """ - - def test_sorted_by_autonow_workaround(self, mpsortedautonow_model): - # workaround - for i in range(1, 5): - mpsortedautonow_model.add_root(desc='node%d' % (i, ), - created=datetime.datetime.now()) - - def test_sorted_by_autonow_FAIL(self, mpsortedautonow_model): - """ - This test asserts that we have a problem. - fix this, somehow - """ - mpsortedautonow_model.add_root(desc='node1') - with pytest.raises(ValueError): - mpsortedautonow_model.add_root(desc='node2') - - -class TestMP_TreeStepOverflow(TestTreeBase): - def test_add_root(self, mpsmallstep_model): - method = mpsmallstep_model.add_root - for i in range(1, 10): - method() - with pytest.raises(PathOverflow): - method() - - def test_add_child(self, mpsmallstep_model): - root = mpsmallstep_model.add_root() - method = root.add_child - for i in range(1, 10): - method() - with pytest.raises(PathOverflow): - method() - - def test_add_sibling(self, mpsmallstep_model): - root = mpsmallstep_model.add_root() - for i in range(1, 10): - root.add_child() - positions = ('first-sibling', 'left', 'right', 'last-sibling') - for pos in positions: - with pytest.raises(PathOverflow): - root.get_last_child().add_sibling(pos) - - def test_move(self, mpsmallstep_model): - root = mpsmallstep_model.add_root() - for i in range(1, 10): - root.add_child() - newroot = mpsmallstep_model.add_root() - targets = [(root, ['first-child', 'last-child']), - (root.get_first_child(), ['first-sibling', - 'left', - 'right', - 'last-sibling'])] - for target, positions in targets: - for pos in positions: - with pytest.raises(PathOverflow): - newroot.move(target, pos) - - -class TestMP_TreeShortPath(TestTreeBase): - """Test a tree with a very small path field (max_length=4) and a - steplen of 1 - """ - - def test_short_path(self, mpshortnotsorted_model): - obj = mpshortnotsorted_model.add_root() - obj = obj.add_child().add_child().add_child() - with pytest.raises(PathOverflow): - obj.add_child() - - -class TestMP_TreeFindProblems(TestTreeBase): - def test_find_problems(self, mpalphabet_model): - mpalphabet_model.alphabet = '01234' - mpalphabet_model(path='01', depth=1, numchild=0, numval=0).save() - mpalphabet_model(path='1', depth=1, numchild=0, numval=0).save() - mpalphabet_model(path='111', depth=1, numchild=0, numval=0).save() - mpalphabet_model(path='abcd', depth=1, numchild=0, numval=0).save() - mpalphabet_model(path='qa#$%!', depth=1, numchild=0, numval=0).save() - mpalphabet_model(path='0201', depth=2, numchild=0, numval=0).save() - mpalphabet_model(path='020201', depth=3, numchild=0, numval=0).save() - mpalphabet_model(path='03', depth=1, numchild=2, numval=0).save() - mpalphabet_model(path='0301', depth=2, numchild=0, numval=0).save() - mpalphabet_model(path='030102', depth=3, numchild=10, numval=0).save() - mpalphabet_model(path='04', depth=10, numchild=1, numval=0).save() - mpalphabet_model(path='0401', depth=20, numchild=0, numval=0).save() - - def got(ids): - return [o.path for o in - mpalphabet_model.objects.filter(id__in=ids)] - - (evil_chars, bad_steplen, orphans, wrong_depth, wrong_numchild) = ( - mpalphabet_model.find_problems()) - assert ['abcd', 'qa#$%!'] == got(evil_chars) - assert ['1', '111'] == got(bad_steplen) - assert ['0201', '020201'] == got(orphans) - assert ['03', '0301', '030102'] == got(wrong_numchild) - assert ['04', '0401'] == got(wrong_depth) - - -class TestMP_TreeFix(TestTreeBase): - - expected_no_holes = { - models.MP_TestNodeShortPath: [ - ('1', 'b', 1, 2), - ('11', 'u', 2, 1), - ('111', 'i', 3, 1), - ('1111', 'e', 4, 0), - ('12', 'o', 2, 0), - ('2', 'd', 1, 0), - ('3', 'g', 1, 0), - ('4', 'a', 1, 4), - ('41', 'a', 2, 0), - ('42', 'a', 2, 0), - ('43', 'u', 2, 1), - ('431', 'i', 3, 1), - ('4311', 'e', 4, 0), - ('44', 'o', 2, 0)], - models.MP_TestSortedNodeShortPath: [ - ('1', 'a', 1, 4), - ('11', 'a', 2, 0), - ('12', 'a', 2, 0), - ('13', 'o', 2, 0), - ('14', 'u', 2, 1), - ('141', 'i', 3, 1), - ('1411', 'e', 4, 0), - ('2', 'b', 1, 2), - ('21', 'o', 2, 0), - ('22', 'u', 2, 1), - ('221', 'i', 3, 1), - ('2211', 'e', 4, 0), - ('3', 'd', 1, 0), - ('4', 'g', 1, 0)]} - expected_with_holes = { - models.MP_TestNodeShortPath: [ - ('1', 'b', 1, 2), - ('13', 'u', 2, 1), - ('134', 'i', 3, 1), - ('1343', 'e', 4, 0), - ('14', 'o', 2, 0), - ('2', 'd', 1, 0), - ('3', 'g', 1, 0), - ('4', 'a', 1, 4), - ('41', 'a', 2, 0), - ('42', 'a', 2, 0), - ('43', 'u', 2, 1), - ('434', 'i', 3, 1), - ('4343', 'e', 4, 0), - ('44', 'o', 2, 0)], - models.MP_TestSortedNodeShortPath: [ - ('1', 'b', 1, 2), - ('13', 'u', 2, 1), - ('134', 'i', 3, 1), - ('1343', 'e', 4, 0), - ('14', 'o', 2, 0), - ('2', 'd', 1, 0), - ('3', 'g', 1, 0), - ('4', 'a', 1, 4), - ('41', 'a', 2, 0), - ('42', 'a', 2, 0), - ('43', 'u', 2, 1), - ('434', 'i', 3, 1), - ('4343', 'e', 4, 0), - ('44', 'o', 2, 0)]} - - def got(self, model): - return [(o.path, o.desc, o.get_depth(), o.get_children_count()) - for o in model.get_tree()] - - def add_broken_test_data(self, model): - model(path='4', depth=2, numchild=2, desc='a').save() - model(path='13', depth=1000, numchild=0, desc='u').save() - model(path='14', depth=4, numchild=500, desc='o').save() - model(path='134', depth=321, numchild=543, desc='i').save() - model(path='1343', depth=321, numchild=543, desc='e').save() - model(path='42', depth=1, numchild=1, desc='a').save() - model(path='43', depth=1000, numchild=0, desc='u').save() - model(path='44', depth=4, numchild=500, desc='o').save() - model(path='434', depth=321, numchild=543, desc='i').save() - model(path='4343', depth=321, numchild=543, desc='e').save() - model(path='41', depth=1, numchild=1, desc='a').save() - model(path='3', depth=221, numchild=322, desc='g').save() - model(path='1', depth=10, numchild=3, desc='b').save() - model(path='2', depth=10, numchild=3, desc='d').save() - - def test_fix_tree_non_destructive(self, mpshort_model): - self.add_broken_test_data(mpshort_model) - mpshort_model.fix_tree(destructive=False) - got = self.got(mpshort_model) - expected = self.expected_with_holes[mpshort_model] - assert got == expected - mpshort_model.find_problems() - - def test_fix_tree_destructive(self, mpshort_model): - self.add_broken_test_data(mpshort_model) - mpshort_model.fix_tree(destructive=True) - got = self.got(mpshort_model) - expected = self.expected_no_holes[mpshort_model] - assert got == expected - mpshort_model.find_problems() - - -class TestIssues(TestTreeBase): - # test for http://code.google.com/p/django-treebeard/issues/detail?id=14 - - def test_many_to_many_django_user_anonymous(self, mpm2muser_model): - # Using AnonymousUser() in the querysets will expose non-treebeard - # related problems in Django 1.0 - # - # Postgres: - # ProgrammingError: can't adapt - # SQLite: - # InterfaceError: Error binding parameter 4 - probably unsupported - # type. - # MySQL compared a string to an integer field: - # `treebeard_mp_testissue14_users`.`user_id` = 'AnonymousUser' - # - # Using a None field instead works (will be translated to IS NULL). - # - # anonuserobj = AnonymousUser() - anonuserobj = None - - def qs_check(qs, expected): - assert [o.name for o in qs] == expected - - def qs_check_first_or_user(expected, root, user): - qs_check( - root.get_children().filter(Q(name="first") | Q(users=user)), - expected) - - user = User.objects.create_user('test_user', 'test@example.com', - 'testpasswd') - user.save() - root = mpm2muser_model.add_root(name="the root node") - - root.add_child(name="first") - second = root.add_child(name="second") - - qs_check(root.get_children(), ['first', 'second']) - qs_check(root.get_children().filter(Q(name="first")), ['first']) - qs_check(root.get_children().filter(Q(users=user)), []) - - qs_check_first_or_user(['first'], root, user) - - qs_check_first_or_user(['first', 'second'], root, anonuserobj) - - user = User.objects.get(username="test_user") - second.users.add(user) - qs_check_first_or_user(['first', 'second'], root, user) - - qs_check_first_or_user(['first'], root, anonuserobj) - - -class TestMoveNodeForm(TestNonEmptyTree): - def _get_nodes_list(self, nodes): - return [(pk, '%sNode %d' % (' ' * 4 * (depth - 1), pk)) - for pk, depth in nodes] - - def _assert_nodes_in_choices(self, form, nodes): - choices = form.fields['_ref_node_id'].choices - assert 0 == choices.pop(0)[0] - assert nodes == [(choice[0], choice[1]) for choice in choices] - - def _move_node_helper(self, node, safe_parent_nodes): - form_class = movenodeform_factory(type(node)) - form = form_class(instance=node) - assert ['desc', '_position', '_ref_node_id'] == list( - form.base_fields.keys()) - got = [choice[0] for choice in form.fields['_position'].choices] - assert ['first-child', 'left', 'right'] == got - nodes = self._get_nodes_list(safe_parent_nodes) - self._assert_nodes_in_choices(form, nodes) - - def _get_node_ids_and_depths(self, nodes): - return [(node.id, node.get_depth()) for node in nodes] - - def test_form_root_node(self, model): - nodes = list(model.get_tree()) - node = nodes.pop(0) - safe_parent_nodes = self._get_node_ids_and_depths(nodes) - self._move_node_helper(node, safe_parent_nodes) - - def test_form_leaf_node(self, model): - nodes = list(model.get_tree()) - node = nodes.pop() - safe_parent_nodes = self._get_node_ids_and_depths(nodes) - self._move_node_helper(node, safe_parent_nodes) - - def test_form_admin(self, model): - request = None - nodes = list(model.get_tree()) - safe_parent_nodes = self._get_node_ids_and_depths(nodes) - for node in model.objects.all(): - site = AdminSite() - form_class = movenodeform_factory(model) - admin_class = admin_factory(form_class) - ma = admin_class(model, site) - got = list(ma.get_form(request).base_fields.keys()) - desc_pos_refnodeid = ['desc', '_position', '_ref_node_id'] - assert desc_pos_refnodeid == got - got = ma.get_fieldsets(request) - expected = [(None, {'fields': desc_pos_refnodeid})] - assert got == expected - got = ma.get_fieldsets(request, node) - assert got == expected - form = ma.get_form(request)() - nodes = self._get_nodes_list(safe_parent_nodes) - self._assert_nodes_in_choices(form, nodes) - - -class TestModelAdmin(TestNonEmptyTree): - def test_default_fields(self, model): - site = AdminSite() - form_class = movenodeform_factory(model) - admin_class = admin_factory(form_class) - ma = admin_class(model, site) - assert list(ma.get_form(None).base_fields.keys()) == [ - 'desc', '_position', '_ref_node_id'] - - -class TestSortedForm(TestTreeSorted): - def test_sorted_form(self, sorted_model): - sorted_model.add_root(val1=3, val2=3, desc='zxy') - sorted_model.add_root(val1=1, val2=4, desc='bcd') - sorted_model.add_root(val1=2, val2=5, desc='zxy') - sorted_model.add_root(val1=3, val2=3, desc='abc') - sorted_model.add_root(val1=4, val2=1, desc='fgh') - sorted_model.add_root(val1=3, val2=3, desc='abc') - sorted_model.add_root(val1=2, val2=2, desc='qwe') - sorted_model.add_root(val1=3, val2=2, desc='vcx') - - form_class = movenodeform_factory(sorted_model) - form = form_class() - assert list(form.fields.keys()) == ['val1', 'val2', 'desc', - '_position', '_ref_node_id'] - - form = form_class(instance=sorted_model.objects.get(desc='bcd')) - assert list(form.fields.keys()) == ['val1', 'val2', 'desc', - '_position', '_ref_node_id'] - assert 'id__position' in str(form) - assert 'id__ref_node_id' in str(form) - - -class TestForm(TestNonEmptyTree): - def test_form(self, model): - form_class = movenodeform_factory(model) - form = form_class() - assert list(form.fields.keys()) == ['desc', '_position', - '_ref_node_id'] - - form = form_class(instance=model.objects.get(desc='1')) - assert list(form.fields.keys()) == ['desc', '_position', - '_ref_node_id'] - assert 'id__position' in str(form) - assert 'id__ref_node_id' in str(form) - - def test_get_position_ref_node(self, model): - form_class = movenodeform_factory(model) - - instance_parent = model.objects.get(desc='1') - form = form_class(instance=instance_parent) - assert form._get_position_ref_node(instance_parent) == { - '_position': 'first-child', - '_ref_node_id': '' - } - - instance_child = model.objects.get(desc='21') - form = form_class(instance=instance_child) - assert form._get_position_ref_node(instance_child) == { - '_position': 'first-child', - '_ref_node_id': model.objects.get(desc='2').pk - } - - instance_grandchild = model.objects.get(desc='22') - form = form_class(instance=instance_grandchild) - assert form._get_position_ref_node(instance_grandchild) == { - '_position': 'right', - '_ref_node_id': model.objects.get(desc='21').pk - } - - instance_grandchild = model.objects.get(desc='231') - form = form_class(instance=instance_grandchild) - assert form._get_position_ref_node(instance_grandchild) == { - '_position': 'first-child', - '_ref_node_id': model.objects.get(desc='23').pk - } - - def test_clean_cleaned_data(self, model): - instance_parent = model.objects.get(desc='1') - _position = 'first-child' - _ref_node_id = '' - form_class = movenodeform_factory(model) - form = form_class( - instance=instance_parent, - data={ - '_position': _position, - '_ref_node_id': _ref_node_id, - 'desc': instance_parent.desc - } - ) - assert form.is_valid() - assert form._clean_cleaned_data() == (_position, _ref_node_id) - - def test_save_edit(self, model): - instance_parent = model.objects.get(desc='1') - original_count = len(model.objects.all()) - form_class = movenodeform_factory(model) - form = form_class( - instance=instance_parent, - data={ - '_position': 'first-child', - '_ref_node_id': model.objects.get(desc='2').pk, - 'desc': instance_parent.desc - } - ) - assert form.is_valid() - saved_instance = form.save() - assert original_count == model.objects.all().count() - assert saved_instance.get_children_count() == 0 - assert saved_instance.get_depth() == 2 - assert not saved_instance.is_root() - assert saved_instance.is_leaf() - - # Return to original state - form_class = movenodeform_factory(model) - form = form_class( - instance=saved_instance, - data={ - '_position': 'first-child', - '_ref_node_id': '', - 'desc': saved_instance.desc - } - ) - assert form.is_valid() - restored_instance = form.save() - assert original_count == model.objects.all().count() - assert restored_instance.get_children_count() == 0 - assert restored_instance.get_depth() == 1 - assert restored_instance.is_root() - assert restored_instance.is_leaf() - - def test_save_new(self, model): - original_count = model.objects.all().count() - assert original_count == 10 - _position = 'first-child' - form_class = movenodeform_factory(model) - form = form_class( - data={'_position': _position, 'desc': 'New Form Test'}) - assert form.is_valid() - assert form.save() is not None - assert original_count < model.objects.all().count() - - -class TestAdminTreeTemplateTags(TestCase): - def test_treebeard_css(self): - template = Template("{% load admin_tree %}{% treebeard_css %}") - context = Context() - rendered = template.render(context) - expected = ('') - assert expected == rendered - - def test_treebeard_js(self): - template = Template("{% load admin_tree %}{% treebeard_js %}") - context = Context() - rendered = template.render(context) - expected = ('' - '' - '' - '') - assert expected == rendered - - def test_get_static_url(self): - with self.settings(STATIC_URL=None, MEDIA_URL=None): - assert get_static_url() == '/' - with self.settings(STATIC_URL='/static/', MEDIA_URL=None): - assert get_static_url() == '/static/' - with self.settings(STATIC_URL=None, MEDIA_URL='/media/'): - assert get_static_url() == '/media/' - with self.settings(STATIC_URL='/static/', MEDIA_URL='/media/'): - assert get_static_url() == '/static/' - - -class TestAdminTree(TestNonEmptyTree): - template = Template('{% load admin_tree %}{% spaceless %}' - '{% result_tree cl request %}{% endspaceless %}') - - def test_result_tree(self, model_without_proxy): - """ - Verifies that inclusion tag result_list generates a table when with - default ModelAdmin settings. - """ - model = model_without_proxy - request = RequestFactory().get('/admin/tree/') - site = AdminSite() - form_class = movenodeform_factory(model) - admin_class = admin_factory(form_class) - m = admin_class(model, site) - list_display = m.get_list_display(request) - list_display_links = m.get_list_display_links(request, list_display) - cl = ChangeList(request, model, list_display, list_display_links, - m.list_filter, m.date_hierarchy, m.search_fields, - m.list_select_related, m.list_per_page, - m.list_max_show_all, m.list_editable, m) - cl.formset = None - context = Context({'cl': cl, - 'request': request}) - table_output = self.template.render(context) - # We have the same amount of drag handlers as objects - drag_handler = ' ' - assert table_output.count(drag_handler) == model.objects.count() - # All nodes are in the result tree - for object in model.objects.all(): - url = cl.url_for_result(object) - node = 'Node %i' % (url, object.pk) - assert node in table_output - # Unfiltered - assert '' in \ - table_output - - def test_unicode_result_tree(self, model_with_unicode): - """ - Verifies that inclusion tag result_list generates a table when with - default ModelAdmin settings. - """ - model = model_with_unicode - # Add a unicode description - model.add_root(desc='áéîøü') - request = RequestFactory().get('/admin/tree/') - site = AdminSite() - form_class = movenodeform_factory(model) - admin_class = admin_factory(form_class) - m = admin_class(model, site) - list_display = m.get_list_display(request) - list_display_links = m.get_list_display_links(request, list_display) - cl = ChangeList(request, model, list_display, list_display_links, - m.list_filter, m.date_hierarchy, m.search_fields, - m.list_select_related, m.list_per_page, - m.list_max_show_all, m.list_editable, m) - cl.formset = None - context = Context({'cl': cl, - 'request': request}) - table_output = self.template.render(context) - # We have the same amount of drag handlers as objects - drag_handler = ' ' - assert table_output.count(drag_handler) == model.objects.count() - # All nodes are in the result tree - for object in model.objects.all(): - url = cl.url_for_result(object) - node = '%s' % (url, object.desc) - assert node in table_output - # Unfiltered - assert '' in \ - table_output - - def test_result_filtered(self, model_without_proxy): - """ Test template changes with filters or pagination. - """ - model = model_without_proxy - # Filtered GET - request = RequestFactory().get('/admin/tree/?desc=1') - site = AdminSite() - form_class = movenodeform_factory(model) - admin_class = admin_factory(form_class) - m = admin_class(model, site) - list_display = m.get_list_display(request) - list_display_links = m.get_list_display_links(request, list_display) - cl = ChangeList(request, model, list_display, list_display_links, - m.list_filter, m.date_hierarchy, m.search_fields, - m.list_select_related, m.list_per_page, - m.list_max_show_all, m.list_editable, m) - cl.formset = None - context = Context({'cl': cl, - 'request': request}) - table_output = self.template.render(context) - # Filtered - assert '' in \ - table_output - - # Not Filtered GET, it should ignore pagination - request = RequestFactory().get('/admin/tree/?p=1') - list_display = m.get_list_display(request) - list_display_links = m.get_list_display_links(request, list_display) - cl = ChangeList(request, model, list_display, list_display_links, - m.list_filter, m.date_hierarchy, m.search_fields, - m.list_select_related, m.list_per_page, - m.list_max_show_all, m.list_editable, m) - cl.formset = None - context = Context({'cl': cl, - 'request': request}) - table_output = self.template.render(context) - # Not Filtered - assert '' in \ - table_output - - # Not Filtered GET, it should ignore all - request = RequestFactory().get('/admin/tree/?all=1') - list_display = m.get_list_display(request) - list_display_links = m.get_list_display_links(request, list_display) - cl = ChangeList(request, model, list_display, list_display_links, - m.list_filter, m.date_hierarchy, m.search_fields, - m.list_select_related, m.list_per_page, - m.list_max_show_all, m.list_editable, m) - cl.formset = None - context = Context({'cl': cl, - 'request': request}) - table_output = self.template.render(context) - # Not Filtered - assert '' in \ - table_output - - -class TestAdminTreeList(TestNonEmptyTree): - template = Template('{% load admin_tree_list %}{% spaceless %}' - '{% result_tree cl request %}{% endspaceless %}') - - def test_result_tree_list(self, model_without_proxy): - """ - Verifies that inclusion tag result_list generates a table when with - default ModelAdmin settings. - """ - model = model_without_proxy - request = RequestFactory().get('/admin/tree/') - site = AdminSite() - form_class = movenodeform_factory(model) - admin_class = admin_factory(form_class) - m = admin_class(model, site) - list_display = m.get_list_display(request) - list_display_links = m.get_list_display_links(request, list_display) - cl = ChangeList(request, model, list_display, list_display_links, - m.list_filter, m.date_hierarchy, m.search_fields, - m.list_select_related, m.list_per_page, - m.list_max_show_all, m.list_editable, m) - cl.formset = None - context = Context({'cl': cl, - 'request': request}) - table_output = self.template.render(context) - - output_template = '
  • Node %i' - for object in model.objects.all(): - expected_output = output_template % (object.pk, object.pk) - assert expected_output in table_output - - def test_result_tree_list_with_action(self, model_without_proxy): - model = model_without_proxy - request = RequestFactory().get('/admin/tree/') - site = AdminSite() - form_class = movenodeform_factory(model) - admin_class = admin_factory(form_class) - m = admin_class(model, site) - list_display = m.get_list_display(request) - list_display_links = m.get_list_display_links(request, list_display) - cl = ChangeList(request, model, list_display, list_display_links, - m.list_filter, m.date_hierarchy, m.search_fields, - m.list_select_related, m.list_per_page, - m.list_max_show_all, m.list_editable, m) - cl.formset = None - context = Context({'cl': cl, - 'request': request, - 'action_form': True}) - table_output = self.template.render(context) - output_template = ('' - 'Node %i') - - for object in model.objects.all(): - expected_output = output_template % (object.pk, object.pk, - object.pk) - assert expected_output in table_output - - - def test_result_tree_list_with_get(self, model_without_proxy): - model = model_without_proxy - # Test t GET parameter with value id - request = RequestFactory().get('/admin/tree/?t=id') - site = AdminSite() - form_class = movenodeform_factory(model) - admin_class = admin_factory(form_class) - m = admin_class(model, site) - list_display = m.get_list_display(request) - list_display_links = m.get_list_display_links(request, list_display) - cl = ChangeList(request, model, list_display, list_display_links, - m.list_filter, m.date_hierarchy, m.search_fields, - m.list_select_related, m.list_per_page, - m.list_max_show_all, m.list_editable, m) - cl.formset = None - context = Context({'cl': cl, - 'request': request}) - table_output = self.template.render(context) - output_template = "opener.dismissRelatedLookupPopup(window, '%i');" - for object in model.objects.all(): - expected_output = output_template % object.pk - assert expected_output in table_output - - -class TestTreeAdmin(TestNonEmptyTree): - site = AdminSite() - - def _create_superuser(self, username): - return User.objects.create(username=username, is_superuser=True) - - def _mocked_authenticated_request(self, url, user): - request_factory = RequestFactory() - request = request_factory.get(url) - request.user = user - return request - - def _mocked_request(self, data): - request_factory = RequestFactory() - request = request_factory.post('/', data=data) - setattr(request, 'session', 'session') - messages = FallbackStorage(request) - setattr(request, '_messages', messages) - return request - - def _get_admin_obj(self, model_class): - form_class = movenodeform_factory(model_class) - admin_class = admin_factory(form_class) - return admin_class(model_class, self.site) - - def test_changelist_view(self): - tmp_user = self._create_superuser('changelist_tmp') - request = self._mocked_authenticated_request('/', tmp_user) - admin_obj = self._get_admin_obj(models.AL_TestNode) - admin_obj.changelist_view(request) - assert admin_obj.change_list_template == 'admin/tree_list.html' - - admin_obj = self._get_admin_obj(models.MP_TestNode) - admin_obj.changelist_view(request) - assert admin_obj.change_list_template != 'admin/tree_list.html' - - def test_get_node(self, model): - admin_obj = self._get_admin_obj(model) - target = model.objects.get(desc='2') - assert admin_obj.get_node(target.pk) == target - - def test_move_node_validate_keyerror(self, model): - admin_obj = self._get_admin_obj(model) - request = self._mocked_request(data={}) - response = admin_obj.move_node(request) - assert response.status_code == 400 - request = self._mocked_request(data={'node_id': 1}) - response = admin_obj.move_node(request) - assert response.status_code == 400 - - def test_move_node_validate_valueerror(self, model): - admin_obj = self._get_admin_obj(model) - request = self._mocked_request(data={'node_id': 1, - 'sibling_id': 2, - 'as_child': 'invalid'}) - response = admin_obj.move_node(request) - assert response.status_code == 400 - - def test_move_validate_missing_nodeorderby(self, model): - node = model.objects.get(desc='231') - admin_obj = self._get_admin_obj(model) - request = self._mocked_request(data={}) - response = admin_obj.try_to_move_node(True, node, 'sorted-child', - request, target=node) - assert response.status_code == 400 - - response = admin_obj.try_to_move_node(True, node, 'sorted-sibling', - request, target=node) - assert response.status_code == 400 - - def test_move_validate_invalid_pos(self, model): - node = model.objects.get(desc='231') - admin_obj = self._get_admin_obj(model) - request = self._mocked_request(data={}) - response = admin_obj.try_to_move_node(True, node, 'invalid_pos', - request, target=node) - assert response.status_code == 400 - - def test_move_validate_to_descendant(self, model): - node = model.objects.get(desc='2') - target = model.objects.get(desc='231') - admin_obj = self._get_admin_obj(model) - request = self._mocked_request(data={}) - response = admin_obj.try_to_move_node(True, node, 'first-sibling', - request, target) - assert response.status_code == 400 - - def test_move_left(self, model): - node = model.objects.get(desc='231') - target = model.objects.get(desc='2') - - admin_obj = self._get_admin_obj(model) - request = self._mocked_request(data={'node_id': node.pk, - 'sibling_id': target.pk, - 'as_child': 0}) - response = admin_obj.move_node(request) - assert response.status_code == 200 - expected = [('1', 1, 0), - ('231', 1, 0), - ('2', 1, 4), - ('21', 2, 0), - ('22', 2, 0), - ('23', 2, 0), - ('24', 2, 0), - ('3', 1, 0), - ('4', 1, 1), - ('41', 2, 0)] - assert self.got(model) == expected - - def test_move_last_child(self, model): - node = model.objects.get(desc='231') - target = model.objects.get(desc='2') - - admin_obj = self._get_admin_obj(model) - request = self._mocked_request(data={'node_id': node.pk, - 'sibling_id': target.pk, - 'as_child': 1}) - response = admin_obj.move_node(request) - assert response.status_code == 200 - expected = [('1', 1, 0), - ('2', 1, 5), - ('21', 2, 0), - ('22', 2, 0), - ('23', 2, 0), - ('24', 2, 0), - ('231', 2, 0), - ('3', 1, 0), - ('4', 1, 1), - ('41', 2, 0)] - assert self.got(model) == expected - diff --git a/wagtail/vendor/django-treebeard/treebeard/tests/urls.py b/wagtail/vendor/django-treebeard/treebeard/tests/urls.py deleted file mode 100644 index ccc4d83a9..000000000 --- a/wagtail/vendor/django-treebeard/treebeard/tests/urls.py +++ /dev/null @@ -1,9 +0,0 @@ -from django.conf.urls import patterns, include, url -from django.contrib import admin - -admin.autodiscover() - -urlpatterns = patterns('', - # Uncomment the next line to enable the admin: - url(r'^admin/', include(admin.site.urls)), -) \ No newline at end of file diff --git a/wagtail/wagtailadmin/forms.py b/wagtail/wagtailadmin/forms.py index 6e30381c1..e55a831f4 100644 --- a/wagtail/wagtailadmin/forms.py +++ b/wagtail/wagtailadmin/forms.py @@ -12,7 +12,7 @@ class SearchForm(forms.Form): if _placeholder is not None: placeholder = _placeholder else: - placeholder = 'Search {}'.format(placeholder_suffix) + placeholder = 'Search {0}'.format(placeholder_suffix) self.fields['q'].widget.attrs = {'placeholder': placeholder} q = forms.CharField(label=_("Search term"), widget=forms.TextInput()) diff --git a/wagtail/wagtailadmin/hooks.py b/wagtail/wagtailadmin/hooks.py index ff167d70d..d299c3346 100644 --- a/wagtail/wagtailadmin/hooks.py +++ b/wagtail/wagtailadmin/hooks.py @@ -1,5 +1,9 @@ from django.conf import settings -from django.utils.importlib import import_module +try: + from importlib import import_module +except ImportError: + # for Python 2.6, fall back on django.utils.importlib (deprecated as of Django 1.7) + from django.utils.importlib import import_module _hooks = {} diff --git a/wagtail/wagtailadmin/static/wagtailadmin/js/page-editor.js b/wagtail/wagtailadmin/static/wagtailadmin/js/page-editor.js index 63f80a26a..f91355961 100644 --- a/wagtail/wagtailadmin/static/wagtailadmin/js/page-editor.js +++ b/wagtail/wagtailadmin/static/wagtailadmin/js/page-editor.js @@ -319,7 +319,7 @@ $(function() { }); /* Set up behaviour of preview button */ - $('#action-preview').click(function() { + $('.action-preview').click(function() { var previewWindow = window.open($(this).data('placeholder'), $(this).data('windowname')); $.ajax({ @@ -349,5 +349,6 @@ $(function() { previewWindow.document.close(); } }); + return false; }); }); diff --git a/wagtail/wagtailadmin/static/wagtailadmin/scss/components/dropdowns.scss b/wagtail/wagtailadmin/static/wagtailadmin/scss/components/dropdowns.scss index 4469a49a0..ee618f377 100644 --- a/wagtail/wagtailadmin/static/wagtailadmin/scss/components/dropdowns.scss +++ b/wagtail/wagtailadmin/static/wagtailadmin/scss/components/dropdowns.scss @@ -6,6 +6,15 @@ cursor:pointer; } + input[type=button], input[type=submit], button, .button{ + padding:1em 0; + display:block; + width:100%; + text-align:left; + padding-left:1em; + } + + ul{ @include unlist(); background-color:$color-teal; @@ -25,7 +34,6 @@ } a{ - box-sizing:border-box; white-space: nowrap; position:relative; @@ -55,7 +63,7 @@ a, input[type=submit], input[type=reset], input[type=button], .button, button{ font-size:0.95em; -webkit-font-smoothing: auto; - text-shadow:-1px -1px 1px rgba(0,0,0,0.2); + @include border-radius(0); } label{ @@ -95,16 +103,7 @@ bottom:100%; } - input[type=button], input[type=submit], button{ - padding:1em 0; - @include border-radius(0); - display:block; - width:100%; - text-align:left; - padding-left:1em; - text-shadow:-1px -1px 1px rgba(0,0,0,0.2); - } - + .button{ float:left; @@ -123,6 +122,7 @@ &:before{ width:1em; + font-size:1.2rem; } &:hover{ @@ -132,6 +132,21 @@ &.open > .button + .dropdown-toggle{ background-color:$color-teal-darker; } + + /* Styles for dropdowns which are also buttons e.g page editor */ + &.dropdown-button{ + .dropdown-toggle{ + @include border-radius(0 3px 3px 0); + } + &.open{ + > input[type=button], > input[type=submit], > button, > .button{ + @include border-radius(0 0 3px 3px); + } + .dropdown-toggle{ + @include border-radius(0 0 3px 0); + } + } + } } .dropdown.white{ @@ -211,7 +226,6 @@ footer .actions .dropdown-toggle{ &:before, &:after{ margin:0; - right:1em !important; } } diff --git a/wagtail/wagtailadmin/static/wagtailadmin/scss/components/explorer.scss b/wagtail/wagtailadmin/static/wagtailadmin/scss/components/explorer.scss index 31be22efe..8ca6ed98f 100644 --- a/wagtail/wagtailadmin/static/wagtailadmin/scss/components/explorer.scss +++ b/wagtail/wagtailadmin/static/wagtailadmin/scss/components/explorer.scss @@ -54,7 +54,7 @@ $explorer-z-index:500; opacity:0.5; left:1em; margin-right:0.5em; - + font-size:1.5em; } &:hover{ @@ -86,6 +86,7 @@ $explorer-z-index:500; display: block; width: 100%; text-align: center; + font-size:1.7em; } &:hover{ diff --git a/wagtail/wagtailadmin/static/wagtailadmin/scss/components/forms.scss b/wagtail/wagtailadmin/static/wagtailadmin/scss/components/forms.scss index d4842874e..9ef27297c 100644 --- a/wagtail/wagtailadmin/static/wagtailadmin/scss/components/forms.scss +++ b/wagtail/wagtailadmin/static/wagtailadmin/scss/components/forms.scss @@ -142,6 +142,7 @@ input[type=checkbox]:checked:before{ /* Core button style */ input[type=submit], input[type=reset], input[type=button], .button, button{ + font-family:Open Sans,Arial,sans-serif; @include border-radius(3px); width:auto; padding:0.7em 1em; @@ -160,8 +161,9 @@ input[type=submit], input[type=reset], input[type=button], .button, button{ overflow:hidden; position:relative; font-weight:normal; + outline:none; -moz-appearance: none; - -moz-box-sizing:content-box; + -moz-box-sizing:border-box; &.button-small{ padding:0.55em 0.8em; @@ -169,15 +171,10 @@ input[type=submit], input[type=reset], input[type=button], .button, button{ } &.button-secondary{ - border:1px solid $color-button; color:$color-button; background-color:white; } - &.icon:before{ - font-size:1.5em; - } - &.icon.text-replace:before{ font-size:auto; } @@ -257,6 +254,13 @@ input[type=submit], input[type=reset], input[type=button], .button, button{ } } +button.icon{ + &:before, + &:after{ + line-height:0; + } +} + .multiple{ @include transition(max-height 10s ease); padding:0; diff --git a/wagtail/wagtailadmin/static/wagtailadmin/scss/components/icons.scss b/wagtail/wagtailadmin/static/wagtailadmin/scss/components/icons.scss index b2d5ddd43..9a1351d9a 100644 --- a/wagtail/wagtailadmin/static/wagtailadmin/scss/components/icons.scss +++ b/wagtail/wagtailadmin/static/wagtailadmin/scss/components/icons.scss @@ -21,7 +21,6 @@ line-height: 1em; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; - font-size:1.8em; text-align:left; vertical-align:middle; margin-right:0.2em; @@ -36,9 +35,7 @@ .hallotoolbar [class*=" icon-"]:before, .hallotoolbar [class*=" icon-"]:before, .hallotoolbar [class^="icon-"]:before{ - font-size:1.1em; vertical-align:-10%; - font-size:1.05em; margin-right:0; } @@ -191,7 +188,7 @@ content:"Q"; } .icon-download:before{ - content:"S"; + content:"S"; /* Credit: Icon made by Freepik from Flaticon.com */ } .icon-order:before{ content:"T"; @@ -225,7 +222,8 @@ content:"3"; } .icon-view:before{ - content:"4"; + content:"4"; /* Credit: Icon made by Zurb from Flaticon.com */ + font-size:1.5rem; } .icon-collapse-down:before{ content:"5"; @@ -259,6 +257,10 @@ } } +.icon.icon-larger:before{ + font-size:1.5em; +} + @keyframes spin { from { transform: rotate(0deg); diff --git a/wagtail/wagtailadmin/static/wagtailadmin/scss/core.scss b/wagtail/wagtailadmin/static/wagtailadmin/scss/core.scss index 812fb00ee..8ffaa450a 100644 --- a/wagtail/wagtailadmin/static/wagtailadmin/scss/core.scss +++ b/wagtail/wagtailadmin/static/wagtailadmin/scss/core.scss @@ -185,6 +185,10 @@ img{ background-color:$color-grey-1; } + a:before{ + font-size:1.2rem; + } + .menu-snippets &.menu-snippets, .menu-users &.menu-users, .menu-snippets &.menu-snippets, @@ -197,20 +201,6 @@ img{ a{ color:white; } - - /* - &:after{ - content: " "; - width: 0px; - height: 0px; - display: block; - position: absolute; - right: 0; - top: 0.9em; - border: 0.7em solid #fff; - border-color: transparent #fff transparent transparent; - } - */ } } diff --git a/wagtail/wagtailadmin/static/wagtailadmin/scss/fonts/wagtail.eot b/wagtail/wagtailadmin/static/wagtailadmin/scss/fonts/wagtail.eot index cdce4afab..f55be6906 100644 Binary files a/wagtail/wagtailadmin/static/wagtailadmin/scss/fonts/wagtail.eot and b/wagtail/wagtailadmin/static/wagtailadmin/scss/fonts/wagtail.eot differ diff --git a/wagtail/wagtailadmin/static/wagtailadmin/scss/fonts/wagtail.svg b/wagtail/wagtailadmin/static/wagtailadmin/scss/fonts/wagtail.svg index 82af03769..188de85f2 100644 --- a/wagtail/wagtailadmin/static/wagtailadmin/scss/fonts/wagtail.svg +++ b/wagtail/wagtailadmin/static/wagtailadmin/scss/fonts/wagtail.svg @@ -51,7 +51,6 @@ - @@ -62,11 +61,12 @@ - + + diff --git a/wagtail/wagtailadmin/static/wagtailadmin/scss/fonts/wagtail.ttf b/wagtail/wagtailadmin/static/wagtailadmin/scss/fonts/wagtail.ttf index 865f2f8b2..a401ae2dd 100644 Binary files a/wagtail/wagtailadmin/static/wagtailadmin/scss/fonts/wagtail.ttf and b/wagtail/wagtailadmin/static/wagtailadmin/scss/fonts/wagtail.ttf differ diff --git a/wagtail/wagtailadmin/static/wagtailadmin/scss/fonts/wagtail.woff b/wagtail/wagtailadmin/static/wagtailadmin/scss/fonts/wagtail.woff index cc36da3d4..fbefc159a 100644 Binary files a/wagtail/wagtailadmin/static/wagtailadmin/scss/fonts/wagtail.woff and b/wagtail/wagtailadmin/static/wagtailadmin/scss/fonts/wagtail.woff differ diff --git a/wagtail/wagtailadmin/static/wagtailadmin/scss/layouts/page-editor.scss b/wagtail/wagtailadmin/static/wagtailadmin/scss/layouts/page-editor.scss index 165ce5a1e..e7fa55c7c 100644 --- a/wagtail/wagtailadmin/static/wagtailadmin/scss/layouts/page-editor.scss +++ b/wagtail/wagtailadmin/static/wagtailadmin/scss/layouts/page-editor.scss @@ -269,8 +269,8 @@ } &.collapsible { - // li.collapsed gets its height from the fieldset only, which is now hidden - // and h2 has position:absolute which doesn't add to it either, so it would be 0 without this + /* li.collapsed gets its height from the fieldset only, which is now hidden + and h2 has position:absolute which doesn't add to it either, so it would be 0 without this */ min-height: 41px; h2{ @@ -306,11 +306,34 @@ } } +footer .preview{ + button, .button{ + background-color:lighten($color-grey-2,10%); + border-color:lighten($color-grey-2,10%); -/* styles applied to an element as it is being reordered */ -.preview{ - padding-top:1em; - padding-bottom:0.5em; + &:hover{ + background-color:$color-grey-2; + border-color:$color-grey-2; + } + } + .dropdown{ + input[type=button], input[type=submit], button, .button{ + background-color:lighten($color-grey-2,10%); + border-color:lighten($color-grey-2,10%); + + &:hover{ + background-color:$color-grey-2; + border-color:$color-grey-2; + } + } + ul, .dropdown-toggle{ + background-color:lighten($color-grey-2,10%); + } + .dropdown-toggle:hover, + &.open > .button + .dropdown-toggle{ + background-color:$color-grey-2; + } + } } @media screen and (min-width: $breakpoint-mobile){ diff --git a/wagtail/wagtailadmin/templates/wagtailadmin/account/password_reset/complete.html b/wagtail/wagtailadmin/templates/wagtailadmin/account/password_reset/complete.html index 8a71eda3a..c0cf872e2 100644 --- a/wagtail/wagtailadmin/templates/wagtailadmin/account/password_reset/complete.html +++ b/wagtail/wagtailadmin/templates/wagtailadmin/account/password_reset/complete.html @@ -1,4 +1,4 @@ -{% extends "wagtailadmin/skeleton.html" %} +{% extends "wagtailadmin/admin_base.html" %} {% load compress %} {% load i18n %} {% block titletag %}{% trans "Reset password" %}{% endblock %} diff --git a/wagtail/wagtailadmin/templates/wagtailadmin/account/password_reset/confirm.html b/wagtail/wagtailadmin/templates/wagtailadmin/account/password_reset/confirm.html index 598761587..5aeae5a71 100644 --- a/wagtail/wagtailadmin/templates/wagtailadmin/account/password_reset/confirm.html +++ b/wagtail/wagtailadmin/templates/wagtailadmin/account/password_reset/confirm.html @@ -1,4 +1,4 @@ -{% extends "wagtailadmin/skeleton.html" %} +{% extends "wagtailadmin/admin_base.html" %} {% load compress %} {% load i18n %} {% block titletag %}{% trans "Set your new password" %}{% endblock %} diff --git a/wagtail/wagtailadmin/templates/wagtailadmin/account/password_reset/done.html b/wagtail/wagtailadmin/templates/wagtailadmin/account/password_reset/done.html index 207d88835..cf1088530 100644 --- a/wagtail/wagtailadmin/templates/wagtailadmin/account/password_reset/done.html +++ b/wagtail/wagtailadmin/templates/wagtailadmin/account/password_reset/done.html @@ -1,4 +1,4 @@ -{% extends "wagtailadmin/skeleton.html" %} +{% extends "wagtailadmin/admin_base.html" %} {% load i18n %} {% load compress %} {% block titletag %}{% trans "Reset password" %}{% endblock %} diff --git a/wagtail/wagtailadmin/templates/wagtailadmin/account/password_reset/form.html b/wagtail/wagtailadmin/templates/wagtailadmin/account/password_reset/form.html index f61035aa2..b9a58edb5 100644 --- a/wagtail/wagtailadmin/templates/wagtailadmin/account/password_reset/form.html +++ b/wagtail/wagtailadmin/templates/wagtailadmin/account/password_reset/form.html @@ -1,7 +1,7 @@ -{% extends "wagtailadmin/skeleton.html" %} +{% extends "wagtailadmin/admin_base.html" %} {% load compress %} {% load i18n %} -{% block titletag %}{ trans "Reset password" %}{% endblock %} +{% block titletag %}{% trans "Reset password" %}{% endblock %} {% block bodyclass %}login{% endblock %} {% block extra_css %} diff --git a/wagtail/wagtailadmin/templates/wagtailadmin/pages/_preview_button_on_create.html b/wagtail/wagtailadmin/templates/wagtailadmin/pages/_preview_button_on_create.html new file mode 100644 index 000000000..7d71ed68b --- /dev/null +++ b/wagtail/wagtailadmin/templates/wagtailadmin/pages/_preview_button_on_create.html @@ -0,0 +1,4 @@ + diff --git a/wagtail/wagtailadmin/templates/wagtailadmin/pages/_preview_button_on_edit.html b/wagtail/wagtailadmin/templates/wagtailadmin/pages/_preview_button_on_edit.html new file mode 100644 index 000000000..b3478c948 --- /dev/null +++ b/wagtail/wagtailadmin/templates/wagtailadmin/pages/_preview_button_on_edit.html @@ -0,0 +1,4 @@ + diff --git a/wagtail/wagtailadmin/templates/wagtailadmin/pages/add_subpage.html b/wagtail/wagtailadmin/templates/wagtailadmin/pages/add_subpage.html index dc1316595..757c013b5 100644 --- a/wagtail/wagtailadmin/templates/wagtailadmin/pages/add_subpage.html +++ b/wagtail/wagtailadmin/templates/wagtailadmin/pages/add_subpage.html @@ -18,7 +18,7 @@
  • diff --git a/wagtail/wagtailadmin/templates/wagtailadmin/pages/create.html b/wagtail/wagtailadmin/templates/wagtailadmin/pages/create.html index fb89e711d..237219940 100644 --- a/wagtail/wagtailadmin/templates/wagtailadmin/pages/create.html +++ b/wagtail/wagtailadmin/templates/wagtailadmin/pages/create.html @@ -25,11 +25,10 @@
    • -
    • + +
    • + {% trans 'Preview' as preview_label %} + {% if display_modes|length > 1 %} + + {% else %} + {% include "wagtailadmin/pages/_preview_button_on_create.html" with label=preview_label icon=1 %} + {% endif %} +
    diff --git a/wagtail/wagtailadmin/templates/wagtailadmin/pages/edit.html b/wagtail/wagtailadmin/templates/wagtailadmin/pages/edit.html index 6aa5aafc8..1e1f7e9b3 100644 --- a/wagtail/wagtailadmin/templates/wagtailadmin/pages/edit.html +++ b/wagtail/wagtailadmin/templates/wagtailadmin/pages/edit.html @@ -27,7 +27,7 @@