diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..5487327b7 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,13 @@ +# top-most EditorConfig file +root = true + +[*] +end_of_line = lf +insert_final_newline = true +charset = utf-8 +trim_trailing_whitespace = true +indent_style = space +indent_size = 2 + +[*.py] +indent_size = 4 diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 000000000..63570efc3 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,27 @@ +module.exports = { + parser: "@babel/eslint-parser", + env: { + browser: true, + es6: true, + node: true, + }, + extends: ["eslint:recommended", "prettier"], + parserOptions: { + ecmaFeatures: { + experimentalObjectRestSpread: true, + jsx: true, + }, + ecmaVersion: 2021, + requireConfigFile: false, + sourceType: "module", + }, + rules: { + "no-unused-vars": [ + "error", + { + argsIgnorePattern: "^_", + varsIgnorePattern: "^_", + }, + ], + }, +} diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 000000000..52b3760de --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,36 @@ +name: Tests + +on: + push: + branches: [main] + pull_request: + branches: [main] + +jobs: + tests: + name: Python ${{ matrix.python-version }} + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + python-version: + - "3.8" + - "3.9" + - "3.10" + - "3.11" + - "3.12" + - "3.13" + + steps: + - uses: actions/checkout@v3 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip wheel setuptools tox + - name: Run tox targets for ${{ matrix.python-version }} + run: | + ENV_PREFIX=$(tr -C -d "0-9" <<< "${{ matrix.python-version }}") + TOXENV=$(tox --listenvs | grep "^py$ENV_PREFIX" | tr '\n' ',') python -m tox diff --git a/.gitignore b/.gitignore index 45f25c901..c02765c74 100644 --- a/.gitignore +++ b/.gitignore @@ -1,18 +1,23 @@ *.pyc *~ .*.swp -\#*# -/secrets.py .DS_Store ._* +.coverage +.tox /FeinCMS.egg-info /MANIFEST /_build /build /dist -tests/test.zip /docs/_build -/tests/.tox +/secrets.py /tests/.coverage +/tests/.tox /tests/htmlcov +\#*# +htmlcov +node_modules +test.zip +tests/test.zip venv diff --git a/.mailmap b/.mailmap index 4cd2bdad0..68466507e 100644 --- a/.mailmap +++ b/.mailmap @@ -13,3 +13,4 @@ Skylar Saveland Stephan Jaekel Simon Bächler Simon Bächler +Matthias Kestenholz diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 000000000..b4c083047 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,40 @@ +exclude: ".yarn/|yarn.lock|\\.min\\.(css|js)$" +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v6.0.0 + hooks: + - id: check-added-large-files + - id: check-builtin-literals + - id: check-executables-have-shebangs + - id: check-merge-conflict + - id: check-toml + - id: check-yaml + - id: detect-private-key + - id: end-of-file-fixer + - id: mixed-line-ending + - id: trailing-whitespace + - repo: https://github.com/adamchainz/django-upgrade + rev: 1.28.0 + hooks: + - id: django-upgrade + args: [--target-version, "3.2"] + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: "v0.13.0" + hooks: + - id: ruff + args: [--unsafe-fixes] + - id: ruff-format + - repo: https://github.com/biomejs/pre-commit + rev: "v2.2.4" + hooks: + - id: biome-check + args: [--unsafe] + verbose: true + - repo: https://github.com/tox-dev/pyproject-fmt + rev: v2.6.0 + hooks: + - id: pyproject-fmt + - repo: https://github.com/abravalheri/validate-pyproject + rev: v0.24.1 + hooks: + - id: validate-pyproject diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 000000000..1c0ca47e9 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,3 @@ +feincms/static/feincms/jquery-1.11.3.min.js +feincms/static/feincms/jquery-ui-1.10.3.custom.min.js +feincms/static/feincms/js.cookie.js diff --git a/.readthedocs.yaml b/.readthedocs.yaml new file mode 100644 index 000000000..bcb0c684e --- /dev/null +++ b/.readthedocs.yaml @@ -0,0 +1,17 @@ +# Read the Docs configuration file +# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details + +version: 2 + +build: + os: ubuntu-24.04 + tools: + python: "3.11" + +sphinx: + configuration: docs/conf.py +# python: +# install: +# - requirements: docs/requirements.txt +# - method: pip +# path: . diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index ea5c5150d..000000000 --- a/.travis.yml +++ /dev/null @@ -1,22 +0,0 @@ -language: python -python: - - "2.6" - - "2.7" - - "3.2" - - "3.3" -env: - - DJANGO_VERSION=1.4.10 - - DJANGO_VERSION=1.5.5 - - DJANGO_VERSION=1.6.0 -matrix: - exclude: - - python: "3.2" - env: DJANGO_VERSION=1.4.10 - - python: "3.3" - env: DJANGO_VERSION=1.4.10 -# command to install dependencies, e.g. pip install -r requirements.txt --use-mirrors -install: - - pip install -q Django==$DJANGO_VERSION django-mptt Pillow feedparser flake8 --use-mirrors - - python setup.py -q install -# command to run tests, e.g. python setup.py test -script: "cd tests && ./manage.py test testapp && flake8 ." diff --git a/AUTHORS b/AUTHORS index e7fa8eec3..0d8f2f02a 100644 --- a/AUTHORS +++ b/AUTHORS @@ -6,85 +6,109 @@ The authors of FeinCMS are: * Simon Meers * Bojan Mihelac * Simon Bächler -* Stephan Jaekel * Bjorn Post +* Stephan Jaekel * Julien Phalip * Daniel Renz -* Skylar Saveland -* Simon Schürpf -* Matt Dawson * Stefan Reinhard -* Simon Schmid +* Matt Dawson +* Simon Schürpf +* Skylar Saveland +* Peter Schmidt * Marc Egli * Psyton +* Simon Schmid * Greg Turner +* Charlie Denton +* saboter +* Greg Taylor * Maarten van Gompel (proycon) * Bjarni Thorisson -* Greg Taylor -* Julian Bez -* Jonas * Antoni Aloy +* Julian Bez * Urs Breton -* Sander van Leeuwen -* Nico Echaniz -* Afonso Fernández Nogueira -* Toby White -* Charlie Denton +* Jonas * Vítor Figueiró +* Fabian Germann * Marc Tamlyn * Martin Mahner * Max Peterson +* Nico Echaniz +* Sander van Leeuwen +* Sebastian Walter +* Toby White +* Afonso Fernández Nogueira * Brian Macdonald -* Maarten Draijer * Eric Delord -* Gabriel Kovacs +* Emmanuelle Delescolle +* Maarten Draijer * adsworth -* Andrew D. Ball * Torkn +* Andrew D. Ball +* Gabriel Kovacs +* Wil Tan +* Perry Roper +* Marco Fucci +* Denis Popov +* Fabian Vogler +* Cellarosi Marco * Raphael Jasjukaitis * Michael Bashkirov -* Fabian Vogler -* Fabian Germann -* Wil Tan -* Vaclav Klecanda -* tayg * Håvard Grimelid -* Cellarosi Marco +* Richard A +* Michael Kutý * Mikhail Korobov +* tayg * Maciek Szczesniak -* Richard A -* Marco Fucci +* Vaclav Klecanda +* valmynd +* Richard Bolt +* Rico Moorman +* sperrygrove +* Saurabh Kumar +* Sebastian Hillig +* svleeuwen +* Silvan Spross +* Artur Barseghyan +* Jay Yu +* Andrin Heusser +* Andrey Popelo +* Andi Albrecht +* Sumit Datta +* Sun Liwen * Tobias Haffner -* Alen Mujezinovic * Alex Kamedov -* Andi Albrecht -* Andrey Popelo -* Andrin Heusser -* Anshuman Bhaduri -* Daniele Procida -* Darryl Woods -* David Evans -* Denis Martinez -* Domas Lapinskas +* Valtron +* Wim Feijen +* Wouter van der Graaf +* antiflu +* feczo * George Karpenkov +* Giorgos Logiotatidis +* Erik Stein +* Gwildor Sok * Harro van der Klauw -* Jay Yu +* Anshuman Bhaduri * Jimmy Ye * Jonas Svensson +* Domas Lapinskas +* Kevin Etienne * Laurent Paoletti * Livio Lunin +* Denis Martinez +* i-trofimtschuk * Marco Cellarosi * Mark Renton +* David Evans +* ilmarsm * Mason Hugus +* Darryl Woods +* Daniele Procida +* niklaushug * Mikkel Hoegh +* Alen Mujezinovic * Olekasnadr Gula * Paul Garner +* Dan Büschlen * Piet Delport * Riccardo Coroneo -* Richard Bolt -* Rico Moorman -* Sebastian Hillig -* Silvan Spross -* Valtron -* Wouter van der Graaf -* svleeuwen diff --git a/CHANGELOG.rst b/CHANGELOG.rst new file mode 100644 index 000000000..d72d9d44d --- /dev/null +++ b/CHANGELOG.rst @@ -0,0 +1,247 @@ +.. _changelog: + +Change log +========== + +Next version +~~~~~~~~~~~~ + +- Added a tinymce 7 integration for the richtext content type. + + +v24.8.1 (2024-08-07) +~~~~~~~~~~~~~~~~~~~~ + +- Fixed another edge case: JPEGs cannot be saved as RGBA. +- Added Django 5.1 to the CI. + + +v24.7.1 (2024-07-10) +~~~~~~~~~~~~~~~~~~~~ + +- Fixed the read the docs build. +- Disabled the CKEditor 4 version nag. + + +v24.4.2 (2024-04-18) +~~~~~~~~~~~~~~~~~~~~ + +- Fixed the filters to work with Django 5. + + +v24.4.1 (2024-04-16) +~~~~~~~~~~~~~~~~~~~~ + +- Forwarded cookies set by ``ApplicationContent`` apps to the final response. +- Added support for webp image formats to the media library. + + +v24.4.0 (2024-04-08) +~~~~~~~~~~~~~~~~~~~~ + +- Fetched the CSRF token value from the input field instead of from the cookie. + This allows making the CSRF cookie ``httponly``. Thanks to Samuel Lim for the + contribution! + + +v23.12.0 (2023-12-22) +~~~~~~~~~~~~~~~~~~~~~ + +- Added Python 3.12, Django 5.0. +- Closed images after reading their dimensions. Raised the logging level to + exception when thumbnailing fails. Thanks to Jeroen Pulles for those two + contributions! + + +`v23.8.0`_ (2023-08-07) +~~~~~~~~~~~~~~~~~~~~~~~ + +.. _v23.8.0: https://github.com/feincms/feincms/compare/v23.1.0...v23.8.0 + +- Made the filter argument of content base's ``get_queryset`` method optional. + This enables easier interoperability of FeinCMS content types with feincms3 + plugins. +- Added Python 3.11. +- Fixed the Pillow resampling constant. + + +`v23.1.0`_ (2023-03-09) +~~~~~~~~~~~~~~~~~~~~~~~ + +.. _v23.1.0: https://github.com/feincms/feincms/compare/v22.4.0...v23.1.0 + +- Fixed a place where ``ACTION_CHECKBOX_NAME`` was imported from the wrong + place. +- Dropped the ``is_dst`` argument to ``timezone.make_aware``. +- Added Django 4.1 and 4.2 to the CI matrix. + + +`v22.4.0`_ (2022-06-02) +~~~~~~~~~~~~~~~~~~~~~~~ + +.. _v22.4.0: https://github.com/feincms/feincms/compare/v22.3.0...v22.4.0 + +- Changed the ``template_key`` field type to avoid boring migrations because of + changing choices. + + +`v22.3.0`_ (2022-05-17) +~~~~~~~~~~~~~~~~~~~~~~~ + +.. _v22.3.0: https://github.com/feincms/feincms/compare/v22.2.0...v22.3.0 + +- The ``render()`` methods of bundled content types have been changed to return + a tuple instead of a HTML fragment in FeinCMS v22.0.0. This was backwards + incompatible in some scenarios. Those methods have been changed to return a + tuple subclass which automatically renders a HTML fragment if evaluated in a + string context. + + +`v22.2.0`_ (2022-05-06) +~~~~~~~~~~~~~~~~~~~~~~~ + +.. _v22.2.0: https://github.com/feincms/feincms/compare/v22.1.0...v22.2.0 + +- Dropped support for Python < 3.8. +- Fixed the thumbnailing support of the ``MediaFileForeignKey``. It has been + broken since Django switched to template-based widget rendering. + + +`v22.1.0`_ (2022-03-31) +~~~~~~~~~~~~~~~~~~~~~~~ + +.. _v22.1.0: https://github.com/feincms/feincms/compare/v22.0.0...v22.1.0 + +- Fixed the ``feincms_render_level`` render recursion protection. +- Wrapped the recursive saving of pages in a transaction, so if anything fails + we have a consistent state. +- Dropped more compatibility code for Django 1.x. +- Made ``medialibrary_orphans`` work again. +- Removed the ``six`` dependency since we're Python 3-only now. +- Updated the pre-commit hooks, cleaned up the JavaScript a bit. + + +`v22.0.0`_ (2022-01-07) +~~~~~~~~~~~~~~~~~~~~~~~ + +.. _v22.0.0: https://github.com/feincms/feincms/compare/v1.20.0...v22.0.0 + +- **Possibly backwards incompatible** Changed all bundled content types' + ``render()`` methods to return the ``(template_name, context)`` tuple instead + of rendering content themselves. +- Dropped compatibility guarantees with Python < 3.6, Django < 3.2. +- Added pre-commit. +- The default view was changed to accept the path as a ``path`` keyword + argument, not only as a positional argument. +- Changed the item editor action buttons CSS to not use transitions so that the + sprite buttons look as they should. + + +`v1.20.0`_ (2021-03-22) +~~~~~~~~~~~~~~~~~~~~~~~ + +- Changed ``#main`` to the more specific ``#feincmsmain`` so that it doesn't + collide with Django's admin panel markup. +- Stopped the JavaScript code from constructing invalid POST action URLs in the + change form. +- Renamed the main branch to main. +- Switched to a declarative setup. +- Switched to GitHub actions. +- Sorted imports. +- Reformated the JavaScript code using prettier. +- Added Python up to 3.9, Django up to the main branch (the upcoming 4.0) to + the CI list. + + +`v1.19.0`_ (2021-03-04) +~~~~~~~~~~~~~~~~~~~~~~~ + +- Fixed a bug where the thumbnailer would try to save JPEGs as RGBA. +- Reformatted the code using black, again. +- Added Python 3.8, Django 3.1 to the build. +- Added the Django 3.2 `.headers` property to the internal dummy response used + in the etag request processor. +- Added a workaround for ``AppConfig``-autodiscovery related crashes. (Because + ``feincms.apps`` now has more meanings). Changed the documentation to prefer + ``feincms.content.application.models.*`` to ``feincms.apps.*``. +- Updated the TinyMCE CDN URL to an version which doesn't show JavaScript + alerts. +- Added missing ``on_delete`` values to the django-filer content types. + + +`v1.18.0`_ (2020-01-21) +~~~~~~~~~~~~~~~~~~~~~~~ + +- Added a style checking job to the CI matrix. +- Dropped compatibility with Django 1.7. + + +`v1.17.0`_ (2019-11-21) +~~~~~~~~~~~~~~~~~~~~~~~ + +- Added compatibility with Django 3.0. + + +`v1.16.0`_ (2019-02-01) +~~~~~~~~~~~~~~~~~~~~~~~ + +- Reformatted everything using black. +- Added a fallback import for the ``staticfiles`` template tag library + which will be gone in Django 3.0. + + +`v1.15.0`_ (2018-12-21) +~~~~~~~~~~~~~~~~~~~~~~~ + +- Actually made use of the timeout specified as + ``FEINCMS_THUMBNAIL_CACHE_TIMEOUT`` instead of the hardcoded value of + seven days. +- Reverted the deprecation of navigation extension autodiscovery. +- Fixed the item editor JavaScript and HTML to work with Django 2.1's + updated inlines. +- Fixed ``TranslatedObjectManager.only_language`` to evaluate callables + before filtering. +- Changed the ``render`` protocol of content types to allow returning a + tuple of ``(ct_template, ct_context)`` which works the same way as + `feincms3's template renderers + `__. + + +`v1.14.0`_ (2018-08-16) +~~~~~~~~~~~~~~~~~~~~~~~ + +- Added a central changelog instead of creating release notes per + release because development is moving more slowly owing to the stable + nature of FeinCMS. +- Fixed history (revision) form, recover form and breadcrumbs when + FeinCMS is used with Reversion 2.0.x. This accommodates refactoring + that took place in `Reversion 1.9 and 2.0 + `_. + If you are upgrading Reversion (rather than starting a new project), + please be aware of the significant interface changes and database + migrations in that product, and attempt upgrading in a development + environment before upgrading a live site. +- Added ``install_requires`` back to ``setup.py`` so that dependencies + are installed automatically again. Note that some combinations of e.g. + Django and django-mptt are incompatible -- look at the `Travis CI + build configuration + `_ to find + out about supported combinations. +- Fixed a few minor compatibility and performance problems. +- Added a new ``FEINCMS_THUMBNAIL_CACHE_TIMEOUT`` setting which allows + caching whether a thumb exists instead of calling ``storage.exists()`` + over and over (which might be slow with remote storages). +- Fixed random reordering of applications by using an ordered dictionary + for apps. +- Increased the length of the caption field for media file translations. +- Fixed ``feincms.contrib.tagging`` to actually work with Django + versions after 1.9.x. + + +.. _v1.14.0: https://github.com/feincms/feincms/compare/v1.13.0...v1.14.0 +.. _v1.15.0: https://github.com/feincms/feincms/compare/v1.14.0...v1.15.0 +.. _v1.16.0: https://github.com/feincms/feincms/compare/v1.15.0...v1.16.0 +.. _v1.17.0: https://github.com/feincms/feincms/compare/v1.16.0...v1.17.0 +.. _v1.18.0: https://github.com/feincms/feincms/compare/v1.17.0...v1.18.0 +.. _v1.19.0: https://github.com/feincms/feincms/compare/v1.18.0...v1.19.0 +.. _v1.20.0: https://github.com/feincms/feincms/compare/v1.19.0...v1.20.0 diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst new file mode 100644 index 000000000..ddf1d89ba --- /dev/null +++ b/CONTRIBUTING.rst @@ -0,0 +1,19 @@ +======================= +Contributing to FeinCMS +======================= + +Submission guidelines +===================== + +If you are creating a pull request, fork the repository and make any changes +in your own feature branch. + +Write and run tests. + + +Code of Conduct +=============== + +This project adheres to the +`Open Code of Conduct `_. +By participating, you are expected to honor this code. diff --git a/LICENSE b/LICENSE index eaf821366..87d53481d 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2009-2013, FEINHEIT GmbH and individual contributors. +Copyright (c) 2009-2014, FEINHEIT GmbH and individual contributors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, diff --git a/MANIFEST.in b/MANIFEST.in index 2b605a533..b8a1e6a53 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,4 +1,5 @@ include AUTHORS +include CONTRIBUTING.rst include LICENSE include MANIFEST.in include README.rst diff --git a/QUICKSTART.rst b/QUICKSTART.rst deleted file mode 100644 index 6c89d9749..000000000 --- a/QUICKSTART.rst +++ /dev/null @@ -1,82 +0,0 @@ -======================================== -Setup FeinCMS in less than five minutes -======================================== - -Quickstart -=============== - -Clone FeinCMS from github (https://github.com/feincms/feincms) into your desired development directory: - -:: - - $ git clone git://github.com/feincms/feincms.git - -Now you find a directory named 'feincms' in your development directory. This is your project directory. Feel free to change its name: - -:: - - $ mv feincms/ myFeinCMSSite - -Now you can choose between the quickstart variant which takes about a minute (depending on your network connection) or the manual setup, which is as easy as the quickstart variant, but let's you understand what your doing. if you are doing this the first time choose the manual variant, as it's taking you only a minute longer. - - -Manual Setup Variant ---------------------- - -Create a directory named 'lib' in your development folder and clone the django-mptt module (https://github.com/django-mptt/django-mptt) into it - -:: - - $ mkdir lib - $ cd lib - $ git clone git://github.com/django-mptt/django-mptt.git - -Change into your project root and create a symbolic link to the downloaded mptt module - -:: - - $ cd ../myFeinCMSSite/ - $ ln -s ../lib/django-mptt/mptt/ mptt - -Step into the example app and start the runserver - -:: - - $ cd example - $ ./manage.py runserver - -The username and password for the examples admin-interface are 'admin' and 'password' - - -Quickstart Variant -------------------- - -Change into your project folder and run the setup script - -:: - - $ cd myFeinCMSSite - $ ./quickstart.sh - -Wait while django and mptt are being fetched and then follow the on-screen instructions to start the runserver and enjoy the installation - -:: - - $ cd example - $ ./manage.py runserver - -The username and password for the examples admin-interface are 'admin' and 'password' - -Further Steps -------------------- - -Feel free to delete all files you do not need for your installation: - -:: - -/docs/ -/MANIFEST.in -/quickstart.sh -/setup.py -/tests/ - diff --git a/README.rst b/README.rst index ea4c30a03..cb0ebc95e 100644 --- a/README.rst +++ b/README.rst @@ -1,7 +1,12 @@ +**NOTE! If you're starting a new project you may want to take a look at feincms3 (https://feincms3.readthedocs.io/). FeinCMS is still maintained and works well, but feincms3 is where current development is happening.** + ======================================== FeinCMS - An extensible Django-based CMS ======================================== +.. image:: https://github.com/feincms/feincms/workflows/Tests/badge.svg + :target: https://github.com/feincms/feincms + When was the last time, that a pre-built software package you wanted to use got many things right, but in the end, you still needed to modify the core parts of the code just because it wasn't (easily) possible to @@ -39,27 +44,27 @@ else which I haven't thought of yet). It provides helper functions, which provide ordered lists of page content blocks. That's all. -Adding your own content types is extremely easy. Do you like textile +Adding your own content types is extremely easy. Do you like markdown that much, that you'd rather die than using a rich text editor? Then add the following code to your project, and you can go on using the CMS without being forced to use whatever the developers deemed best: -:: +.. code-block:: python + from markdown2 import markdown from feincms.module.page.models import Page - from django.contrib.markup.templatetags.markup import textile from django.db import models - class TextilePageContent(models.Model): + class MarkdownPageContent(models.Model): content = models.TextField() class Meta: abstract = True def render(self, **kwargs): - return textile(self.content) + return markdown(self.content) - Page.create_content_type(TextilePageContent) + Page.create_content_type(MarkdownPageContent) That's it. Not even ten code lines for your own page content type. @@ -73,38 +78,10 @@ Visit these sites ----------------- * FeinCMS Website: http://www.feincms.org/ -* Read the documentation: http://feincms-django-cms.readthedocs.org/ +* Read the documentation: https://feincms-django-cms.readthedocs.io/ * See the Google Groups page at http://groups.google.com/group/django-feincms * FeinCMS on github: https://github.com/feincms/feincms/ -Try out FeinCMS in a Box ------------------------- - -`FeinCMS in a Box `_ is a -prepackaged installation of FeinCMS with a few additional modules and a setup -script. Try it out! - -IRC ---- - -Visit us on IRC! We are hanging around in ``#feincms`` on freenode. If you -do not have an IRC client you can use the -`freenode Web IRC client `_. - -Quickstart ----------- - -You can find a short quickstart guide at QUICKSTART.rst - -Optional Packages ------------------ - -* `pytidylib `_ can be - installed to perform HTML validation and cleanup using `HTML Tidy - `_ while editing content. Install pytidylib and - add ``FEINCMS_TIDY_HTML = True`` to your settings.py. - - Repository branches ------------------- @@ -112,9 +89,9 @@ The FeinCMS repository on github has several branches. Their purpose and rewinding policies are described below. * ``maint``: Maintenance branch for the second-newest version of FeinCMS. -* ``master``: Stable version of FeinCMS. +* ``main``: Stable version of FeinCMS. -``master`` and ``maint`` are never rebased or rewound. +``main`` and ``maint`` are never rebased or rewound. * ``next``: Upcoming version of FeinCMS. This branch is rarely rebased if ever, but this might happen. A note will be sent to the official @@ -122,12 +99,3 @@ rewinding policies are described below. * ``pu`` or feature branches are used for short-lived projects. These branches aren't guaranteed to stay around and are not meant to be deployed into production environments. - - -Travis CI -========= - -.. image:: https://travis-ci.org/feincms/feincms.png?branch=next - :target: https://travis-ci.org/feincms/feincms -.. image:: https://travis-ci.org/feincms/feincms.png?branch=master - :target: https://travis-ci.org/feincms/feincms diff --git a/biome.json b/biome.json new file mode 100644 index 000000000..915669572 --- /dev/null +++ b/biome.json @@ -0,0 +1,61 @@ +{ + "$schema": "https://biomejs.dev/schemas/2.2.3/schema.json", + "assist": { "actions": { "source": { "organizeImports": "off" } } }, + "formatter": { + "enabled": true, + "indentStyle": "space", + "indentWidth": 2 + }, + "linter": { + "enabled": true, + "rules": { + "recommended": true, + "a11y": { + "noSvgWithoutTitle": "off" + }, + "complexity": { + "noImportantStyles": "off" + }, + "correctness": { + "noInnerDeclarations": "off", + "noUndeclaredVariables": "warn", + "noUnknownTypeSelector": "warn", + "noUnusedImports": "error", + "noUnusedVariables": "error", + "useHookAtTopLevel": "error" + }, + "security": { + "noDangerouslySetInnerHtml": "warn" + }, + "style": { + "noDescendingSpecificity": "warn", + "noParameterAssign": "off", + "useForOf": "warn", + "useArrayLiterals": "error" + }, + "suspicious": { + "noArrayIndexKey": "warn", + "noAssignInExpressions": "off" + } + } + }, + "javascript": { + "formatter": { + "semicolons": "asNeeded" + }, + "globals": ["django", "CKEDITOR"] + }, + "css": { + "formatter": { + "enabled": true + }, + "linter": { + "enabled": true + } + }, + "json": { + "formatter": { + "enabled": false + } + } +} diff --git a/docs/_theme/nature/static/nature.css_t b/docs/_theme/nature/static/nature.css_t deleted file mode 100644 index 03b0379d0..000000000 --- a/docs/_theme/nature/static/nature.css_t +++ /dev/null @@ -1,229 +0,0 @@ -/** - * Sphinx stylesheet -- default theme - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - */ - -@import url("basic.css"); - -/* -- page layout ----------------------------------------------------------- */ - -body { - font-family: Arial, sans-serif; - font-size: 100%; - background-color: #111; - color: #555; - margin: 0; - padding: 0; -} - -div.documentwrapper { - float: left; - width: 100%; -} - -div.bodywrapper { - margin: 0 0 0 230px; -} - -hr{ - border: 1px solid #B1B4B6; -} - -div.document { - background-color: #eee; -} - -div.body { - background-color: #ffffff; - color: #3E4349; - padding: 0 30px 30px 30px; - font-size: 0.8em; -} - -div.footer { - color: #555; - width: 100%; - padding: 13px 0; - text-align: center; - font-size: 75%; -} - -div.footer a { - color: #444; - text-decoration: underline; -} - -div.related { - background-color: #6BA81E; - line-height: 32px; - color: #fff; - text-shadow: 0px 1px 0 #444; - font-size: 0.80em; -} - -div.related a { - color: #E2F3CC; -} - -div.sphinxsidebar { - font-size: 0.75em; - line-height: 1.5em; -} - -div.sphinxsidebarwrapper{ - padding: 20px 0; -} - -div.sphinxsidebar h3, -div.sphinxsidebar h4 { - font-family: Arial, sans-serif; - color: #222; - font-size: 1.2em; - font-weight: normal; - margin: 0; - padding: 5px 10px; - background-color: #ddd; - text-shadow: 1px 1px 0 white -} - -div.sphinxsidebar h4{ - font-size: 1.1em; -} - -div.sphinxsidebar h3 a { - color: #444; -} - - -div.sphinxsidebar p { - color: #888; - padding: 5px 20px; -} - -div.sphinxsidebar p.topless { -} - -div.sphinxsidebar ul { - margin: 10px 20px; - padding: 0; - color: #000; -} - -div.sphinxsidebar a { - color: #444; -} - -div.sphinxsidebar input { - border: 1px solid #ccc; - font-family: sans-serif; - font-size: 1em; -} - -div.sphinxsidebar input[type=text]{ - margin-left: 20px; -} - -/* -- body styles ----------------------------------------------------------- */ - -a { - color: #005B81; - text-decoration: none; -} - -a:hover { - color: #E32E00; - text-decoration: underline; -} - -div.body h1, -div.body h2, -div.body h3, -div.body h4, -div.body h5, -div.body h6 { - font-family: Arial, sans-serif; - background-color: #BED4EB; - font-weight: normal; - color: #212224; - margin: 30px 0px 10px 0px; - padding: 5px 0 5px 10px; - text-shadow: 0px 1px 0 white -} - -div.body h1 { border-top: 20px solid white; margin-top: 0; font-size: 200%; } -div.body h2 { font-size: 150%; background-color: #C8D5E3; } -div.body h3 { font-size: 120%; background-color: #D8DEE3; } -div.body h4 { font-size: 110%; background-color: #D8DEE3; } -div.body h5 { font-size: 100%; background-color: #D8DEE3; } -div.body h6 { font-size: 100%; background-color: #D8DEE3; } - -a.headerlink { - color: #c60f0f; - font-size: 0.8em; - padding: 0 4px 0 4px; - text-decoration: none; -} - -a.headerlink:hover { - background-color: #c60f0f; - color: white; -} - -div.body p, div.body dd, div.body li { - line-height: 1.5em; -} - -div.admonition p.admonition-title + p { - display: inline; -} - -div.highlight{ - background-color: white; -} - -div.note { - background-color: #eee; - border: 1px solid #ccc; -} - -div.seealso { - background-color: #ffc; - border: 1px solid #ff6; -} - -div.topic { - background-color: #eee; -} - -div.warning { - background-color: #ffe4e4; - border: 1px solid #f66; -} - -p.admonition-title { - display: inline; -} - -p.admonition-title:after { - content: ":"; -} - -pre { - padding: 10px; - background-color: White; - color: #222; - line-height: 1.2em; - border: 1px solid #C6C9CB; - font-size: 1.2em; - margin: 1.5em 0 1.5em 0; - -webkit-box-shadow: 1px 1px 1px #d8d8d8; - -moz-box-shadow: 1px 1px 1px #d8d8d8; -} - -tt { - background-color: #ecf0f3; - color: #222; - padding: 1px 2px; - font-size: 1.2em; - font-family: monospace; -} diff --git a/docs/_theme/nature/static/pygments.css b/docs/_theme/nature/static/pygments.css deleted file mode 100644 index 652b76128..000000000 --- a/docs/_theme/nature/static/pygments.css +++ /dev/null @@ -1,54 +0,0 @@ -.c { color: #999988; font-style: italic } /* Comment */ -.k { font-weight: bold } /* Keyword */ -.o { font-weight: bold } /* Operator */ -.cm { color: #999988; font-style: italic } /* Comment.Multiline */ -.cp { color: #999999; font-weight: bold } /* Comment.preproc */ -.c1 { color: #999988; font-style: italic } /* Comment.Single */ -.gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */ -.ge { font-style: italic } /* Generic.Emph */ -.gr { color: #aa0000 } /* Generic.Error */ -.gh { color: #999999 } /* Generic.Heading */ -.gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */ -.go { color: #111 } /* Generic.Output */ -.gp { color: #555555 } /* Generic.Prompt */ -.gs { font-weight: bold } /* Generic.Strong */ -.gu { color: #aaaaaa } /* Generic.Subheading */ -.gt { color: #aa0000 } /* Generic.Traceback */ -.kc { font-weight: bold } /* Keyword.Constant */ -.kd { font-weight: bold } /* Keyword.Declaration */ -.kp { font-weight: bold } /* Keyword.Pseudo */ -.kr { font-weight: bold } /* Keyword.Reserved */ -.kt { color: #445588; font-weight: bold } /* Keyword.Type */ -.m { color: #009999 } /* Literal.Number */ -.s { color: #bb8844 } /* Literal.String */ -.na { color: #008080 } /* Name.Attribute */ -.nb { color: #999999 } /* Name.Builtin */ -.nc { color: #445588; font-weight: bold } /* Name.Class */ -.no { color: #ff99ff } /* Name.Constant */ -.ni { color: #800080 } /* Name.Entity */ -.ne { color: #990000; font-weight: bold } /* Name.Exception */ -.nf { color: #990000; font-weight: bold } /* Name.Function */ -.nn { color: #555555 } /* Name.Namespace */ -.nt { color: #000080 } /* Name.Tag */ -.nv { color: purple } /* Name.Variable */ -.ow { font-weight: bold } /* Operator.Word */ -.mf { color: #009999 } /* Literal.Number.Float */ -.mh { color: #009999 } /* Literal.Number.Hex */ -.mi { color: #009999 } /* Literal.Number.Integer */ -.mo { color: #009999 } /* Literal.Number.Oct */ -.sb { color: #bb8844 } /* Literal.String.Backtick */ -.sc { color: #bb8844 } /* Literal.String.Char */ -.sd { color: #bb8844 } /* Literal.String.Doc */ -.s2 { color: #bb8844 } /* Literal.String.Double */ -.se { color: #bb8844 } /* Literal.String.Escape */ -.sh { color: #bb8844 } /* Literal.String.Heredoc */ -.si { color: #bb8844 } /* Literal.String.Interpol */ -.sx { color: #bb8844 } /* Literal.String.Other */ -.sr { color: #808000 } /* Literal.String.Regex */ -.s1 { color: #bb8844 } /* Literal.String.Single */ -.ss { color: #bb8844 } /* Literal.String.Symbol */ -.bp { color: #999999 } /* Name.Builtin.Pseudo */ -.vc { color: #ff99ff } /* Name.Variable.Class */ -.vg { color: #ff99ff } /* Name.Variable.Global */ -.vi { color: #ff99ff } /* Name.Variable.Instance */ -.il { color: #009999 } /* Literal.Number.Integer.Long */ \ No newline at end of file diff --git a/docs/_theme/nature/theme.conf b/docs/_theme/nature/theme.conf deleted file mode 100644 index 1cc400446..000000000 --- a/docs/_theme/nature/theme.conf +++ /dev/null @@ -1,4 +0,0 @@ -[theme] -inherit = basic -stylesheet = nature.css -pygments_style = tango diff --git a/docs/advanced/base.rst b/docs/advanced/base.rst index 76605d03c..3b50bca08 100644 --- a/docs/advanced/base.rst +++ b/docs/advanced/base.rst @@ -15,7 +15,7 @@ manage content with the :class:`~feincms.admin.item_editor.ItemEditor`. .. attribute:: Base.content Beware not to name subclass field `content` as this will overshadow `ContentProxy` and you will - not be able to reference `ContentProxy`. + not be able to reference `ContentProxy`. .. method:: Base.create_content_type(model, regions=None, [**kwargs]) diff --git a/docs/advanced/caching.rst b/docs/advanced/caching.rst index 80229319a..cf4f2b6cd 100644 --- a/docs/advanced/caching.rst +++ b/docs/advanced/caching.rst @@ -31,20 +31,20 @@ of helper methods and variables, ready to be used in your templates. Here's an (incomplete) list of variables to use in {% cache %} blocks [#djangocache]_: - * feincms_page.cache_key -- a string describing the current page. + * feincms_page.cache_key -- DEPRECATED as of FeinCMS 1.11. + A string describing the current page. Depending on the extensions loaded, this varies with the page, the page's modification date, its language, etc. This is always a safe bet to use on page specific fragments. - + * LANGUAGE_CODE -- even if two requests are asking for the same page, the html code rendered might differ in translated elements in the navigation or elsewhere. If the fragment varies on language, include LANGUAGE_CODE in the cache specifier. - + * request.user.id -- different users might be allowed to see different views of the site. Add request.user.id to the cache specifier if this is the case. -.. [#djangocache] Please see the django documentation for detailed +.. [#djangocache] Please see the django documentation for detailed description of the {% cache %} template tag. - diff --git a/docs/advanced/utils.rst b/docs/advanced/utils.rst index ee45edc8b..6edf49d60 100644 --- a/docs/advanced/utils.rst +++ b/docs/advanced/utils.rst @@ -15,8 +15,3 @@ MyClass = get_object('module.MyClass') myfunc = get_object('anothermodule.module2.my_function', fail_silently=True) - -.. function:: collect_dict_values(data) - - Converts a list of 2-tuples to a dict. - diff --git a/docs/conf.py b/docs/conf.py index 26aa1b611..ec2f3cd0f 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # FeinCMS documentation build configuration file, created by # sphinx-quickstart on Mon Aug 10 17:03:33 2009. @@ -14,12 +13,10 @@ import os import sys + # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. -sys.path.append(os.path.abspath('.')) -sys.path.append(os.path.abspath('..')) -os.environ['DJANGO_SETTINGS_MODULE'] = 'example.settings' # -- General configuration ----------------------------------------------------- @@ -28,20 +25,20 @@ extensions = [] # Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] +templates_path = ["_templates"] # The suffix of source filenames. -source_suffix = '.rst' +source_suffix = ".rst" # The encoding of source files. -#source_encoding = 'utf-8' +# source_encoding = 'utf-8' # The master toctree document. -master_doc = 'index' +master_doc = "index" # General information about the project. -project = u'FeinCMS' -copyright = u'2009-2010, Feinheit GmbH and contributors' +project = "FeinCMS" +copyright = "2009-2010, Feinheit GmbH and contributors" # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the @@ -49,153 +46,159 @@ # # The short X.Y version. sys.path.insert(0, os.path.dirname(os.path.dirname(__file__))) -import feincms +import feincms # noqa + -version = '.'.join(map(str, feincms.VERSION)) +version = ".".join(map(str, feincms.VERSION)) # The full version, including alpha/beta/rc tags. release = feincms.__version__ # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. -#language = None +# language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: -#today = '' +# today = '' # Else, today_fmt is used as the format for a strftime call. -#today_fmt = '%B %d, %Y' +# today_fmt = '%B %d, %Y' # List of documents that shouldn't be included in the build. -#unused_docs = [] +# unused_docs = [] # List of directories, relative to source directory, that shouldn't be searched # for source files. -exclude_trees = ['_build'] +exclude_trees = ["_build"] # The reST default role (used for this markup: `text`) to use for all documents. -#default_role = None +# default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). -#add_module_names = True +# add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. -#show_authors = False +# show_authors = False # The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' +pygments_style = "sphinx" # A list of ignored prefixes for module index sorting. -#modindex_common_prefix = [] +# modindex_common_prefix = [] # -- Options for HTML output --------------------------------------------------- # The theme to use for HTML and HTML Help pages. Major themes that come with # Sphinx are currently 'default' and 'sphinxdoc'. -html_theme_path = ['_theme'] -html_theme = 'nature' +# html_theme_path = ['_theme'] +# html_theme = "sphinx_rtd_theme" # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. -#html_theme_options = {} +# html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. -#html_theme_path = [] +# html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". -#html_title = None +# html_title = None # A shorter title for the navigation bar. Default is the same as html_title. -#html_short_title = None +# html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. -#html_logo = None +# html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. -#html_favicon = None +# html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] +html_static_path = ["_static"] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. -#html_last_updated_fmt = '%b %d, %Y' +# html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. -#html_use_smartypants = True +# html_use_smartypants = True # Custom sidebar templates, maps document names to template names. -#html_sidebars = {} +# html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. -#html_additional_pages = {} +# html_additional_pages = {} # If false, no module index is generated. -#html_use_modindex = True +# html_use_modindex = True # If false, no index is generated. -#html_use_index = True +# html_use_index = True # If true, the index is split into individual pages for each letter. -#html_split_index = False +# html_split_index = False # If true, links to the reST sources are added to the pages. -#html_show_sourcelink = True +# html_show_sourcelink = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. -#html_use_opensearch = '' +# html_use_opensearch = '' # If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml"). -#html_file_suffix = '' +# html_file_suffix = '' # Output file base name for HTML help builder. -htmlhelp_basename = 'FeinCMSdoc' +htmlhelp_basename = "FeinCMSdoc" # -- Options for LaTeX output -------------------------------------------------- # The paper size ('letter' or 'a4'). -latex_paper_size = 'a4' +latex_paper_size = "a4" # The font size ('10pt', '11pt' or '12pt'). -latex_font_size = '10pt' +latex_font_size = "10pt" # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). -latex_documents = [( - 'index', 'FeinCMS.tex', u'FeinCMS Documentation', - u'Feinheit GmbH and contributors', 'manual'), +latex_documents = [ + ( + "index", + "FeinCMS.tex", + "FeinCMS Documentation", + "Feinheit GmbH and contributors", + "manual", + ), ] # The name of an image file (relative to this directory) to place at the top of # the title page. -#latex_logo = None +# latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. -#latex_use_parts = False +# latex_use_parts = False # Additional stuff for the LaTeX preamble. -#latex_preamble = '' +# latex_preamble = '' # Documents to append as an appendix to all manuals. -#latex_appendices = [] +# latex_appendices = [] # If false, no module index is generated. -#latex_use_modindex = True +# latex_use_modindex = True diff --git a/docs/contenttypes.rst b/docs/contenttypes.rst index 89c27bd04..e9fadb201 100644 --- a/docs/contenttypes.rst +++ b/docs/contenttypes.rst @@ -82,23 +82,23 @@ in the rendered result. outlined apply for all other CMS base types. -The complete code required to implement and include a custom textile content +The complete code required to implement and include a custom markdown content type is shown here:: + from markdown2 import markdown from feincms.module.page.models import Page - from django.contrib.markup.templatetags.markup import textile from django.db import models - class TextilePageContent(models.Model): + class MarkdownPageContent(models.Model): content = models.TextField() class Meta: abstract = True def render(self, **kwargs): - return textile(self.content) + return markdown(self.content) - Page.create_content_type(TextilePageContent) + Page.create_content_type(MarkdownPageContent) There are three field names you should not use because they are added @@ -313,15 +313,6 @@ Application content Used to let the administrator freely integrate 3rd party applications into the CMS. Described in :ref:`integration-applicationcontent`. - -Comments content ----------------- -.. module:: feincms.content.comments.models -.. class:: CommentsContent() - -Comment list and form using ``django.contrib.comments``. - - Contact form content -------------------- .. module:: feincms.content.contactform.models @@ -354,7 +345,7 @@ Additional arguments for :func:`~feincms.models.Base.create_content_type`: Media library integration ------------------------- -.. module:: feincms.content.medialibrary.v2 +.. module:: feincms.module.medialibrary.contents .. class:: MediaFileContent() Mini-framework for arbitrary file types with customizable rendering @@ -392,7 +383,7 @@ Additional arguments for :func:`~feincms.models.Base.create_content_type`: Raw content ----------- -.. module:: feincms.content.raw.models +.. module:: feincms.contents .. class:: RawContent() Raw HTML code, f.e. for flash movies or javascript code. @@ -400,47 +391,62 @@ Raw HTML code, f.e. for flash movies or javascript code. Rich text --------- -.. module:: feincms.content.richtext.models +.. module:: feincms.contents .. class:: RichTextContent() -Rich text editor widget, stripped down to the essentials; no media support, only -a few styles activated. The necessary javascript files are not included, -you need to put them in the right place on your own. +Rich text editor widget, stripped down to the essentials; no media support, +only a few styles activated. -By default, ``RichTextContent`` expects a TinyMCE activation script at -``js/tiny_mce/tiny_mce.js``. This can be customized by overriding -``FEINCMS_RICHTEXT_INIT_TEMPLATE`` and ``FEINCMS_RICHTEXT_INIT_CONTEXT`` in -your ``settings.py`` file. +By default, ``RichTextContent`` uses the CDN-served version of TinyMCE 4.1. +This can be customized by overriding ``FEINCMS_RICHTEXT_INIT_TEMPLATE`` and +``FEINCMS_RICHTEXT_INIT_CONTEXT`` in your ``settings.py`` file. If you only want to provide a different path to the TinyMCE javascript file, you can do this as follows:: FEINCMS_RICHTEXT_INIT_CONTEXT = { 'TINYMCE_JS_URL': '/your_custom_path/tiny_mce.js', - } - -If you pass cleanse=True to the create_content_type invocation for your -RichTextContent types, the HTML code will be cleansed right before saving -to the database everytime the content is modified. + } Additional arguments for :func:`~feincms.models.Base.create_content_type`: * ``cleanse``: - Whether the HTML code should be cleansed of all tags and attributes - which are not explicitly whitelisted. The default is ``False``. + Either the dotted python path to a function or a function itself which + accepts a HTML string and returns a cleansed version of it. A library which + is often used for this purpose is + `feincms-cleanse `_. -RSS feeds ---------- -.. module:: feincms.content.rss.models -.. class:: RSSContent +CKEditor +~~~~~~~~ + +Add the following settings to activate `CKEditor `_:: + + FEINCMS_RICHTEXT_INIT_TEMPLATE = 'admin/content/richtext/init_ckeditor.html' + FEINCMS_RICHTEXT_INIT_CONTEXT = { + # See http://cdn.ckeditor.com/ for the latest version: + 'CKEDITOR_JS_URL': '//cdn.ckeditor.com/4.4.2/standard/ckeditor.js', + } + + +Other rich text libraries +~~~~~~~~~~~~~~~~~~~~~~~~~ + +Other rich text widgets can be wired up for the RichTextContent. +You would have to write two functions: One which is called when +rich text editing functionality is added ("richify"), and another +one which is called when functionality is removed ("poorify"). +The second is necessary because rich text editors do not like +being dragged; when dragging a rich text content type, it is first +poorified and then richified again as soon as the content type has +been dropped into its final position. -A feed reader widget. This also serves as an example how to build a content -type that needs additional processing, in this case from a cron job. If an -RSS feed has been added to the CMS, ``manage.py update_rsscontent`` should -be run periodically (either through a cron job or through other means) to -keep the shown content up to date. The `feedparser` module is required. +To perform those operations + * Add a function adding the new rich text editor to + ``contentblock_init_handlers`` and to ``contentblock_move_handlers.richify`` + * Add a function removing the rich text editor to + ``contentblock_move_handlers.poorify`` Section content @@ -453,17 +459,16 @@ Combined rich text editor, title and media file. Template content ---------------- -.. module:: feincms.content.template.models +.. module:: feincms.contents .. class:: TemplateContent() -This is a content type that just includes a snippet from a template. -This content type scans all template directories for templates below -``content/template/`` and allows the user to select one of these templates -which are then rendered using the Django template language. +This is a content type that just renders a template. The available +templates have to be specified when creating the content type:: -Note that some file extensions are automatically filtered so they will not -appear in the list, namely any filenames ending with ``.~`` or ``.tmp`` will -be ignored. + Page.create_content_type(TemplateContent, TEMPLATES=( + ('content/template/something1.html', _('Something 1')), + ('content/template/something2.html', _('Something 2')), + )) Also note that a template content is not sandboxed or specially rendered. Whatever a django template can do a TemplateContent snippet can do too, @@ -525,7 +530,7 @@ do is adding a classmethod named :func:`initialize_type` to your content type, a pass additional keyword arguments to :func:`create_content_type`. If you want to see an example of these two uses, have a look at the -:class:`~feincms.content.medialibrary.v2.MediaFileContent`. +:class:`~feincms.content.medialibrary.models.MediaFileContent`. It is generally recommended to use this hook to configure content types compared to putting the configuration into the site-wide settings file. This @@ -548,13 +553,13 @@ You could take advantage of the fact that ``create_content_type`` returns the created model:: from feincms.module.page.models import Page - from feincms.content.raw.models import RawContent + from feincms.contents import RawContent PageRawContent = Page.create_content_type(RawContent) Or you could use :func:`content_type_for`:: - from feincms.content.raw.models import RawContent + from feincms.contents import RawContent PageRawContent = Page.content_type_for(RawContent) diff --git a/docs/contributing.rst b/docs/contributing.rst index 7f9ca1991..4feed80a0 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -12,9 +12,9 @@ The FeinCMS repository on github has several branches. Their purpose and rewinding policies are described below. * ``maint``: Maintenance branch for the second-newest version of FeinCMS. -* ``master``: Stable version of FeinCMS. +* ``main``: Stable version of FeinCMS. -``master`` and ``maint`` are never rebased or rewound. +``main`` and ``maint`` are never rebased or rewound. * ``next``: Upcoming version of FeinCMS. This branch is rarely rebased if ever, but this might happen. A note will be sent to the official diff --git a/docs/deprecation.rst b/docs/deprecation.rst index 36dc07668..12c6d8aaa 100644 --- a/docs/deprecation.rst +++ b/docs/deprecation.rst @@ -101,3 +101,28 @@ will be issued for at least two releases. * The ``_feincms_extensions`` attribute on the page model and on models inheriting ``ExtensionsMixin`` is gone. + + +1.10 +==== + +No deprecations. + + +1.11 +==== + +* ``RSSContent`` and ``update_rsscontent`` have been deprecated. + +* The automatic discovery of subclasses of ``NavigationExtension`` has been + replaced with an explicit mechanism of defining navigation extensions. + +* ``Page.cache_key`` has never been used by FeinCMS itself and will therefore + be removed in a future release. Comparable functionality has been available + for a long time with ``Page.path_to_cache_key``. + + +1.12 +==== + +* TODO update this diff --git a/docs/extensions.rst b/docs/extensions.rst index 979328d3c..55fa96dcf 100644 --- a/docs/extensions.rst +++ b/docs/extensions.rst @@ -74,4 +74,4 @@ is as follows: Only model and admin instances which inherit from :class:`~feincms.extensions.ExtensionsMixin` and :class:`~feincms.extensions.ExtensionModelAdmin` can be extended - this way. \ No newline at end of file + this way. diff --git a/docs/faq.rst b/docs/faq.rst index d804a4ab1..e6f323788 100644 --- a/docs/faq.rst +++ b/docs/faq.rst @@ -30,21 +30,3 @@ too big, it might be time to reconsider whether you really want to use the extension mechanism or if it might not be easier to start freshly, only using the editor admin classes, feincms.models.Base and maybe parts of the included PageManager... - - - -I run ``syncdb`` and get a message about missing columns in the page table -========================================================================== - -You enabled the page module (added :mod:`feincms.module.page` to -``INSTALLED_APPS``), run syncdb, and afterwards registered a few -extensions. The extensions you activated -(:mod:`~feincms.module.page.extensions.datepublisher` and -:mod:`~feincms.module.page.extensions.translations`) add new fields to -the page model, but your first ``syncdb`` did not know about them and -therefore did not create the columns for those extensions. - -You can either remove the line ``Page.register_extensions(...)`` from -your code or drop the page_page table and re-run ``syncdb``. If you want -to keep the pages you've already created, you need to figure out the -correct ALTER TABLE statements for your database yourself. diff --git a/docs/index.rst b/docs/index.rst index 7b92fbaae..b5110b395 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -15,25 +15,25 @@ to a region (f.e. the sidebar, the main content region or something else which I haven't thought of yet). It provides helper functions, which provide ordered lists of page content blocks. That's all. -Adding your own content types is extremely easy. Do you like textile +Adding your own content types is extremely easy. Do you like markdown that much, that you'd rather die than using a rich text editor? Then add the following code to your project, and you can go on using the CMS without being forced to use whatever the developers deemed best:: + from markdown2 import markdown from feincms.module.page.models import Page - from django.contrib.markup.templatetags.markup import textile from django.db import models - class TextilePageContent(models.Model): + class MarkdownPageContent(models.Model): content = models.TextField() class Meta: abstract = True def render(self, **kwargs): - return textile(self.content) + return markdown(self.content) - Page.create_content_type(TextilePageContent) + Page.create_content_type(MarkdownPageContent) That's it. Only ten lines of code for your own page content type. @@ -54,6 +54,7 @@ Contents integration medialibrary templatetags + settings migrations versioning advanced/index @@ -62,20 +63,27 @@ Contents deprecation -Releases -======== +.. include:: ../CHANGELOG.rst + + +Old release notes +================= .. toctree:: :maxdepth: 1 - releases/1.2 - releases/1.3 - releases/1.4 - releases/1.5 - releases/1.6 - releases/1.7 - releases/1.8 + releases/1.13 + releases/1.12 + releases/1.11 + releases/1.10 releases/1.9 + releases/1.8 + releases/1.7 + releases/1.6 + releases/1.5 + releases/1.4 + releases/1.3 + releases/1.2 Indices and tables diff --git a/docs/installation.rst b/docs/installation.rst index f9890c51e..e607bccf2 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -9,31 +9,37 @@ Installation This document describes the steps needed to install FeinCMS. -FeinCMS requires a working installation of Django_ version 1.4, 1.5 or 1.6. See -the Django_ documentation for how to install and configure Django. +FeinCMS requires a working installation of Django_ version 1.7 or better +See the Django_ documentation for how to install and configure Django. You can download a stable release of FeinCMS using ``pip``. Pip will install feincms and its dependencies. Dependencies which are automatically installed -are: feedparser_, Pillow_ and django-mptt_. +are: feedparser_, Pillow_ and django-mptt_. For outdated versions of +Django the best place to find supported combinations of library versions is the +`tox build configuration +`_. $ pip install feincms -In order to install documentation, tests or an example project, install from -the Git_ repository instead:: +In order to install documentation and tests install from the Git_ repository +instead:: $ git clone git://github.com/feincms/feincms.git +Please be aware that feincms3 is being developed as a separate, new project. +For new CMS projects I’m more likely to use django-content-editor and feincms3. +You can read more about feincms3 on https://feincms3.readthedocs.io/en/latest/ + If you are looking to implement a blog, check out elephantblog_. You will also need a Javascript WYSIWYG editor of your choice (Not included). -TinyMCE_ works out of the box and is recommended. +TinyMCE_ and CKEditor_ work out of the box and are recommended. .. _Django: http://www.djangoproject.com/ .. _Git: http://git-scm.com/ .. _Subversion: http://subversion.tigris.org/ .. _django-mptt: http://github.com/django-mptt/django-mptt/ -.. _django-tagging: http://code.google.com/p/django-tagging/ .. _feedparser: http://www.feedparser.org/ .. _Pillow: https://pypi.python.org/pypi/Pillow/ .. _elephantblog: http://github.com/feincms/feincms-elephantblog diff --git a/docs/integration.rst b/docs/integration.rst index 6883bfd2e..47de52696 100644 --- a/docs/integration.rst +++ b/docs/integration.rst @@ -15,19 +15,19 @@ The default CMS handler view is ``feincms.views.cbv.handler``. You can add the following as last line in your ``urls.py`` to make a catch-all for any pages which were not matched before:: - from feincms.views.cbv.views import Handler + from feincms.views import Handler handler = Handler.as_view() - urlpatterns += patterns('', + urlpatterns += [ url(r'^$', handler, name='feincms_home'), url(r'^(.*)/$', handler, name='feincms_handler'), - ) + ] Note that this default handler can also take a keyword parameter ``path`` to specify which url to render. You can use that functionality to implement a default page by adding another entry to your ``urls.py``:: - from feincms.views.cbv.views import Handler + from feincms.views import Handler handler = Handler.as_view() ... @@ -41,9 +41,9 @@ of your own URL patterns like this:: # ... - urlpatterns += patterns('', + urlpatterns += [ url(r'', include('feincms.urls')), - ) + ] The URLconf entry names ``feincms_home`` and ``feincms_handler`` must both exist somewhere in your project. The standard ``feincms.urls`` @@ -95,20 +95,20 @@ can be too easily violated. An example ``urls.py`` follows:: - from django.conf.urls import patterns, include, url + from django.conf.urls import include, url from django.views.generic.detail import DetailView from django.views.generic.list import ListView from news.models import Entry - urlpatterns = patterns('', + urlpatterns = [ url(r'^$', ListView.as_view( queryset=Entry.objects.all(), ), name='entry_list'), url(r'^(?P[^/]+)/$', DetailView.as_view( queryset=Entry.objects.all(), ), name='entry_detail'), - ) + ] Please note that you should not add the ``news/`` prefix here. You should *not* reference this ``urls.py`` file anywhere in a ``include`` statement. @@ -124,7 +124,7 @@ It's as simple as that:: Page.create_content_type(ApplicationContent, APPLICATIONS=( ('news.urls', 'News application'), - )) + )) Writing the models @@ -135,12 +135,11 @@ reachable through standard means (remember, they aren't ``include``\d anywhere) it's not possible to use standard ``reverse`` calls to determine the absolute URL of a news entry. FeinCMS provides its own ``app_reverse`` function (see :ref:`integration-reversing-urls` for -details) and ``permalink`` decorator mimicking the interface of -Django's standard functionality:: +details) mimicking the interface of Django's standard functionality:: from django.db import models - from feincms.content.application import models as app_models + from feincms.content.application.models import app_reverse class Entry(models.Model): title = models.CharField(max_length=200) @@ -153,26 +152,22 @@ Django's standard functionality:: def __str__(self): return self.title - @app_models.permalink def get_absolute_url(self): - return ('entry_detail', 'news.urls', (), { + return app_reverse('entry_detail', 'news.urls', kwargs={ 'slug': self.slug, - }) + }) The only difference is that you do not only have to specify the view name -(``entry_detail``) but also the URLconf file (``news.urls``) for this -specific ``permalink`` decorator. The URLconf string must correspond to the -specification used in the ``APPLICATIONS`` list in the ``create_content_type`` -call. +(``entry_detail``) but also the URLconf file (``news.urls``). The URLconf +string must correspond to the specification used in the ``APPLICATIONS`` +list in the ``create_content_type`` call. .. note:: - Previous FeinCMS versions only provided a monkey patched ``reverse`` + Old FeinCMS versions only provided a monkey patched ``reverse`` method with a slightly different syntax for reversing URLs. This - behavior is still available and as of now (FeinCMS 1.5) still active - by default. It is recommended to start using the new way right now - and add ``FEINCMS_REVERSE_MONKEY_PATCH = False`` to your settings file. + behavior has been removed some time ago. Returning content from views @@ -246,12 +241,14 @@ of any template rendering calls: ``urls.py``:: - from django.conf.urls import patterns, include, url + from django.conf.urls import url - urlpatterns = patterns('news.views', - url(r'^$', 'entry_list', name='entry_list'), - url(r'^(?P[^/]+)/$', 'entry_detail', name='entry_detail'), - ) + from news.views import entry_list, entry_detail + + urlpatterns = [ + url(r'^$', entry_list, name='entry_list'), + url(r'^(?P[^/]+)/$', entry_detail, name='entry_detail'), + ] The two templates referenced, ``news/entry_list.html`` and @@ -270,7 +267,7 @@ content. .. note:: Older versions of FeinCMS only offered fragments for a similar purpose. They - are still suported, but it's recommended you switch over to this style instead. + are still supported, but it's recommended you switch over to this style instead. .. warning:: @@ -344,8 +341,8 @@ need them. All of these must be specified in the ``APPLICATIONS`` argument to Page.create_content_type(ApplicationContent, APPLICATIONS=( ('registration', 'Account creation and management', { 'urls': 'yourapp.registration_urls', - }), - ) + }), + ) * ``admin_fields``: Adding more fields to the application content interface: @@ -362,15 +359,15 @@ need them. All of these must be specified in the ``APPLICATIONS`` argument to required=False, initial=form.instance.parameters.get('exclusive_subpages', True), help_text=_('Exclude everything other than the application\'s content when rendering subpages.'), - ), - } + ), + } Page.create_content_type(ApplicationContent, APPLICATIONS=( ('registration', 'Account creation and management', { 'urls': 'yourapp.registration_urls', 'admin_fields': registration_admin_fields, - }), - ) + }), + ) The form fields will only be visible after saving the ``ApplicationContent`` for the first time. They are stored inside a JSON-encoded field. The values @@ -438,26 +435,33 @@ You don't need to do anything else as long as you use the built-in :: - from feincms.module.page.extensions.navigation import NavigationExtension, PagePretender + from feincms.module.page.extensions import navigation - class BlogCategoriesNavigationExtension(NavigationExtension): + class BlogCategoriesNavigationExtension(navigation.NavigationExtension): name = _('blog categories') def children(self, page, **kwargs): for category in Category.objects.all(): - yield PagePretender( + yield navigation.PagePretender( title=category.name, url=category.get_absolute_url(), - ) + ) - class PassthroughExtension(NavigationExtension): + class PassthroughExtension(navigation.NavigationExtension): name = 'passthrough extension' def children(self, page, **kwargs): for p in page.children.in_navigation(): yield p - Page.register_extensions('feincms.module.page.extensions.navigation') + class MyExtension(navigation.Extension): + navigation_extensions = [ + BlogCategoriesNavigationExtension, + PassthroughExtension, + # Alternatively, a dotted Python path also works. + ] + + Page.register_extensions(MyExtension) Note that the objects returned should at least try to mimic a real page so navigation template tags as ``siblings_along_path_to`` and friends continue @@ -474,5 +478,3 @@ to work, ie. at least the following attributes should exist: level = page.level+1 lft = page.lft rght = page.rght - - diff --git a/docs/medialibrary.rst b/docs/medialibrary.rst index 48f26887f..4be37efdc 100644 --- a/docs/medialibrary.rst +++ b/docs/medialibrary.rst @@ -19,12 +19,12 @@ add :mod:`feincms.module.medialibrary` to your ``INSTALLED_APPS`` setting, and create a content type for a media file as follows:: from feincms.module.page.models import Page - from feincms.content.medialibrary.v2 import MediaFileContent + from feincms.content.medialibrary.contents import MediaFileContent Page.create_content_type(MediaFileContent, TYPE_CHOICES=( - ('default', _('default')), - ('lightbox', _('lightbox')), - )) + ('default', _('default')), + ('lightbox', _('lightbox')), + )) ``TYPE_CHOICES`` has nothing to do with file types -- it's about choosing @@ -54,24 +54,24 @@ Rendering media file contents A set of recognition functions will be run on the file name to determine the file type. Using combinations of the name and type, the default render method tries to find a template for rendering the -:class:`~feincms.content.medialibrary.models.MediaFileContent`. +:class:`~feincms.content.medialibrary.models.MediaFileBase`. The default set of pre-defined content types and recognition functions is:: MediaFileBase.register_filetypes( - ('image', _('Image'), lambda f: re.compile(r'\.(bmp|jpe?g|jp2|jxr|gif|png|tiff?)$', re.IGNORECASE).search(f)), - ('video', _('Video'), lambda f: re.compile(r'\.(mov|m[14]v|mp4|avi|mpe?g|qt|ogv|wmv)$', re.IGNORECASE).search(f)), - ('audio', _('Audio'), lambda f: re.compile(r'\.(au|mp3|m4a|wma|oga|ram|wav)$', re.IGNORECASE).search(f)), - ('pdf', _('PDF document'), lambda f: f.lower().endswith('.pdf')), - ('swf', _('Flash'), lambda f: f.lower().endswith('.swf')), - ('txt', _('Text'), lambda f: f.lower().endswith('.txt')), - ('rtf', _('Rich Text'), lambda f: f.lower().endswith('.rtf')), - ('zip', _('Zip archive'), lambda f: f.lower().endswith('.zip')), - ('doc', _('Microsoft Word'), lambda f: re.compile(r'\.docx?$', re.IGNORECASE).search(f)), - ('xls', _('Microsoft Excel'), lambda f: re.compile(r'\.xlsx?$', re.IGNORECASE).search(f)), - ('ppt', _('Microsoft PowerPoint'), lambda f: re.compile(r'\.pptx?$', re.IGNORECASE).search(f)), - ('other', _('Binary'), lambda f: True), # Must be last - ) + ('image', _('Image'), lambda f: re.compile(r'\.(bmp|jpe?g|jp2|jxr|gif|png|tiff?)$', re.IGNORECASE).search(f)), + ('video', _('Video'), lambda f: re.compile(r'\.(mov|m[14]v|mp4|avi|mpe?g|qt|ogv|wmv)$', re.IGNORECASE).search(f)), + ('audio', _('Audio'), lambda f: re.compile(r'\.(au|mp3|m4a|wma|oga|ram|wav)$', re.IGNORECASE).search(f)), + ('pdf', _('PDF document'), lambda f: f.lower().endswith('.pdf')), + ('swf', _('Flash'), lambda f: f.lower().endswith('.swf')), + ('txt', _('Text'), lambda f: f.lower().endswith('.txt')), + ('rtf', _('Rich Text'), lambda f: f.lower().endswith('.rtf')), + ('zip', _('Zip archive'), lambda f: f.lower().endswith('.zip')), + ('doc', _('Microsoft Word'), lambda f: re.compile(r'\.docx?$', re.IGNORECASE).search(f)), + ('xls', _('Microsoft Excel'), lambda f: re.compile(r'\.xlsx?$', re.IGNORECASE).search(f)), + ('ppt', _('Microsoft PowerPoint'), lambda f: re.compile(r'\.pptx?$', re.IGNORECASE).search(f)), + ('other', _('Binary'), lambda f: True), # Must be last + ) You can add to that set by calling ``MediaFile.register_filetypes()`` with your new file types similar to the above. @@ -115,8 +115,9 @@ To have a thumbnail preview in your ModelAdmin and Inline class:: class ImageForProject(models.Model): project = models.ForeignKey(Project) - mediafile = MediaFileForeignKey(MediaFile, related_name='+', - limit_choices_to={'type': 'image'}) + mediafile = MediaFileForeignKey( + MediaFile, related_name='+', + limit_choices_to={'type': 'image'}) For the maginfying-glass select widget in your content type inherit your inline @@ -127,4 +128,3 @@ from FeinCMSInline:: class MyContent(models.Model): feincms_item_editor_inline = MyContentInline - diff --git a/docs/migrations.rst b/docs/migrations.rst index bd6de9905..ce17272c3 100644 --- a/docs/migrations.rst +++ b/docs/migrations.rst @@ -1,40 +1,36 @@ .. _migrations: -================================================= -Database migration support for FeinCMS with South -================================================= +====================================== +Database migration support for FeinCMS +====================================== -If you don't know what South_ is you should probably go and read about -it right now! -.. _South: http://south.aeracode.org/ +FeinCMS itself does not come with any migrations. It is recommended that you +add migrations for FeinCMS models yourself inside your project. -FeinCMS itself does not come with any migrations. It does not have to: Its -core models haven't changed for several versions now. This does not mean South -isn't supported! You are free to use South to manage FeinCMS' models which -is a very useful technique especially if you are using :ref:`page-extensions`. +Django's builtin migrations +=========================== -The following steps should be sufficient to get up and running with South -in your project: +This guide assumes that you are using both the page and the medialibrary +module from FeinCMS. Simply leave out medialibrary if unused. -* Put a copy of South somewhere on your ``PYTHONPATH``, with ``pip``, ``hg`` - or whatever pleases you most. -* Add ``'south'`` to ``INSTALLED_APPS``. -* Create a new folder in your app with an empty ``__init__.py`` file inside, - e.g. ``yourapp/migrate/``. -* Add the following configuration variable to your ``settings.py``:: +* Create a new folder named ``migrate`` in your app with an empty + ``__init__.py`` inside. +* Add the following configuration to your ``settings.py``:: - SOUTH_MIGRATION_MODULES = { - 'page': 'yourapp.migrate.page', - 'medialibrary': 'yourapp.migrate.medialibrary', # if you are using the medialibrary - # which comes with FeinCMS - } - -* Run ``./manage.py convert_to_south page`` and ``./manage.py convert_to_south medialibrary`` -* That's it! + MIGRATION_MODULES = { + 'page': 'yourapp.migrate.page', + 'medialibrary': 'yourapp.migrate.medialibrary', + } .. warning:: You **must not** use ``migrations`` as folder name for the FeinCMS - migrations, otherwise South **will** get confused. + migrations, otherwise Django **will** get confused. + +* Create initial migrations and apply them:: + + ./manage.py makemigrations medialibrary + ./manage.py makemigrations page + ./manage.py migrate diff --git a/docs/page.rst b/docs/page.rst index 513be9f1d..18e19c94d 100644 --- a/docs/page.rst +++ b/docs/page.rst @@ -21,7 +21,8 @@ To activate the page module, you need to follow the instructions in :ref:`installation` and afterwards add :mod:`feincms.module.page` to your :data:`INSTALLED_APPS`. -Before proceeding with ``manage.py syncdb``, it might be a good idea to take +Before proceeding with ``manage.py makemigrations`` and ``./manage.py migrate``, +it might be a good idea to take a look at :ref:`page-extensions` -- the page module does have the minimum of features in the default configuration and you will probably want to enable several extensions. @@ -33,16 +34,16 @@ be to create :class:`~feincms.content.medialibrary.models.MediaFileContent` and by adding the following lines somewhere into your project, for example in a ``models.py`` file that will be processed anyway:: - from django.utils.translation import ugettext_lazy as _ + from django.utils.translation import gettext_lazy as _ from feincms.module.page.models import Page - from feincms.content.richtext.models import RichTextContent - from feincms.content.medialibrary.models import MediaFileContent + from feincms.contents import RichTextContent + from feincms.module.medialibrary.contents import MediaFileContent Page.register_extensions( - 'feincms.module.extensions.datepublisher', - 'feincms.module.extensions.translations' - ) # Example set of extensions + 'feincms.extensions.datepublisher', + 'feincms.extensions.translations' + ) # Example set of extensions Page.register_templates({ 'title': _('Standard template'), @@ -50,14 +51,14 @@ by adding the following lines somewhere into your project, for example in a 'regions': ( ('main', _('Main content area')), ('sidebar', _('Sidebar'), 'inherited'), - ), - }) + ), + }) Page.create_content_type(RichTextContent) Page.create_content_type(MediaFileContent, TYPE_CHOICES=( ('default', _('default')), ('lightbox', _('lightbox')), - )) + )) It will be a good idea most of the time to register the @@ -84,6 +85,11 @@ richtext support:: 'TINYMCE_JS_URL': STATIC_URL + 'your_custom_path/tiny_mce.js', } +If you want to use a different admin site, or want to apply customizations to +the admin class used, add the following setting to your site-wide settings:: + + FEINCMS_USE_PAGE_ADMIN = False + Wiring up the views =================== @@ -92,9 +98,9 @@ Just add the following lines to your ``urls.py`` to get a catch-all URL pattern: :: - urlpatterns += patterns('', + urlpatterns += [ url(r'', include('feincms.urls')), - ) + ] If you want to define a page as home page for the whole site, you can give it @@ -159,37 +165,42 @@ upon registering the extension. The :func:`register` method receives the :class:`~feincms.module.page.modeladmins.PageAdmin` as arguments. The extensions can be activated as follows:: - Page.register_extensions('feincms.module.page.extensions.navigation', - 'feincms.module.page.extensions.titles', - 'feincms.module.extensions.translations') + Page.register_extensions( + 'feincms.module.page.extensions.navigation', + 'feincms.module.page.extensions.titles', + 'feincms.extensions.translations') The following extensions are available currently: -* :mod:`feincms.module.extensions.changedate` --- Creation and modification dates +* :mod:`feincms.extensions.changedate` --- Creation and modification dates Adds automatically maintained creation and modification date fields to the page. -* :mod:`feincms.module.extensions.ct_tracker` --- Content type cache +* :mod:`feincms.extensions.ct_tracker` --- Content type cache - Helps reduce database queries if you have three or more content types. + Helps reduce database queries if you have three or more content types by + caching in the database which content types are available on each page. + If this extension is used, ``Page._ct_inventory`` has to be nullified + after adding and/or removing content blocks, otherwise changes might not + be visible in the frontend. Saving the page instance accomplishes this. -* :mod:`feincms.module.extensions.datepublisher` --- Date-based publishing +* :mod:`feincms.extensions.datepublisher` --- Date-based publishing Adds publication date and end date fields to the page, thereby enabling the administrator to define a date range where a page will be available to website visitors. -* :mod:`feincms.module.page.extensions.excerpt` --- Page summary +* :mod:`feincms.page.extensions.excerpt` --- Page summary Add a brief excerpt summarizing the content of this page. -* :mod:`feincms.module.extensions.featured` --- Simple featured flag for a page +* :mod:`feincms.extensions.featured` --- Simple featured flag for a page Lets administrators set a featured flag that lets you treat that page special. @@ -202,12 +213,19 @@ The following extensions are available currently: this extension. +* :mod:`feincms.module.page.extensions.navigationgroups` --- Navigation groups + + Adds a navigation group field to each page which can be used to distinguish + between the header and footer (or meta) navigation. Filtering is achieved + by passing the ``group`` argument to ``feincms_nav``. + + * :mod:`feincms.module.page.extensions.relatedpages` --- Links related content Add a many-to-many relationship field to relate this page to other pages. -* :mod:`feincms.module.extensions.seo` --- Search engine optimization +* :mod:`feincms.extensions.seo` --- Search engine optimization Adds fields to the page relevant for search engine optimization (SEO), currently only meta keywords and description. @@ -233,7 +251,7 @@ The following extensions are available currently: content area. -* :mod:`feincms.module.extensions.translations` --- Page translations +* :mod:`feincms.extensions.translations` --- Page translations Adds a language field and a recursive translations many to many field to the page, so that you can define the language the page is in and assign @@ -255,8 +273,7 @@ The following extensions are available currently: .. note:: These extension modules add new fields to the ``Page`` class. If you add or - remove page extensions after you've run ``syncdb`` for the first time you - have to change the database schema yourself, or use :ref:`migrations`. + remove page extensions you make and apply new migrations. Using page request processors @@ -274,7 +291,7 @@ This allows for various actions dependent on page and request, for example a simple user access check can be implemented like this:: def authenticated_request_processor(page, request): - if not request.user.is_authenticated(): + if not request.user.is_authenticated: raise django.core.exceptions.PermissionDenied Page.register_request_processor(authenticated_request_processor) @@ -372,10 +389,10 @@ Feincms site, add the following to your top-level urls.py:: from feincms.module.page.sitemap import PageSitemap sitemaps = {'pages' : PageSitemap} - urlpatterns += patterns('', + urlpatterns += [ url(r'^sitemap\.xml$', 'django.contrib.sitemaps.views.sitemap', {'sitemaps': sitemaps}), - ) + ] This will produce a default sitemap at the /sitemap.xml url. A sitemap can be further customised by passing it appropriate parameters, like so:: diff --git a/docs/releases/1.10.rst b/docs/releases/1.10.rst new file mode 100644 index 000000000..2c58d37d0 --- /dev/null +++ b/docs/releases/1.10.rst @@ -0,0 +1,125 @@ +========================== +FeinCMS 1.10 release notes +========================== + +Welcome to FeinCMS 1.10! + + +Full compatibility with the app-loading refactor in Django 1.7 +============================================================== + +FeinCMS 1.10 is compatible with Django 1.7. It shouldn't even be necessary +to change your FeinCMS-using code. + +The only change is that we cannot test for name clashes in content types +anymore. This only concerns you if you were using more than one +``feincms.models.Base`` subclass inside the same app using the same content +types. You should be using the ``class_name`` argument to +``create_content_type`` already anyway. The technical reason for this change +is that the name clash test requires the app registry to be ready (which it +isn't yet when creating content types). + +.. note:: + + If for some reason FeinCMS 1.10 does not work with Django 1.7 for you, + please provide a description of the problems you're seeing in the + `issue tracker `_. + + +TinyMCE 4.1 as default rich text configuration +============================================== + +FeinCMS now uses TinyMCE 4.1 as the default rich text editor instead of the +outdated 3.x line of releases. The previous version is still officially +supported as is CKEditor. Also, the TinyMCE configuration has been changed to +hide the menubar by default. + + +Home-grown schema checking support has been deactivated +======================================================= + +Not many words needed. Use South or Django 1.7's own migrations support +instead. + +This functionality has been deactivated without a deprecation period because +compared to real database migration solutions it looks more like a bug than a +feature. Also, it interfered with initial migrations -- the page table's +schema was introspected because of other ``post_syncdb`` signals before the +table itself even existed under certain circumstances, which meant that +migrations failed with the only way out being ``syncdb --all``. + +To continue using this functionality, set ``FEINCMS_CHECK_DATABASE_SCHEMA`` +to ``True``. The functionality will be completely removed in the next release. + + +A better mechanism for assigning pages to different menu bars +============================================================= + +The new extension :mod:`feincms.module.page.extensions.navigationgroups` +allows assigning pages to different regions, and asking ``feincms_nav`` to +only return pages belonging to one of those. + +The default configuration of the extension defines two regions, ``default`` +and ``footer``. This can be easily changed by subclassing the extension class +and overriding the ``groups`` attribute and registering this extension class +instead of the original. + +An example for getting a list of pages for the footer region follows:: + + {% load feincms_page_tags %} + {% feincms_nav feincms_page level=2 group='footer' as footer_nav %} + + +Backwards-incompatible changes +============================== + +Shouldn't have any. + + +Removal of deprecated features +------------------------------ + +None. + + +New deprecations +================ + +None. + + + +Notable features and improvements +================================= + +* The bundled versions of jQuery and jQuery UI have been updated to 1.11.1 + and 1.10.3 respectively. + +* ``feincms_languagelinks`` does not return page URLs leading to inactive + pages anymore. + +* The application content type form does not mysteriously forget values + anymore. + +* ``Page.get_original_translation`` returns the current page instead of + crashing if there is no original translation. + +* ``feincms_nav`` returns an empty list instead of crashing if the page + argument is ``None``. + +* Settings are documented again. + + +Bugfixes +======== + +* Bulk deletion of pages in the tree editor shouldn't lead to MPTT data + corruption anymore. This didn't happen often before either, but shouldn't + happen anymore at all. + + +Compatibility with Django and other apps +======================================== + +FeinCMS 1.10 requires Django 1.4 or better. The testsuite is successfully run +against Django 1.4, 1.5, 1.6 and the upcoming 1.7. diff --git a/docs/releases/1.11.rst b/docs/releases/1.11.rst new file mode 100644 index 000000000..9dff918cc --- /dev/null +++ b/docs/releases/1.11.rst @@ -0,0 +1,139 @@ +========================== +FeinCMS 1.11 release notes +========================== + +Welcome to FeinCMS 1.11! + + +Template inheritance with application contents +============================================== + +FeinCMS adds a decorator and a ``TemplateResponse`` subclass which can be +returned from apps embedded through ``ApplicationContent``. The template +response's template will override the template used by FeinCMS' main view and +the context will be merged. A selection of HTTP response headers +(currently *Cache-Control*, *Last-Modified* and *Expires*) will also be copied +to the main response. The following two examples are fully equivalent:: + + from django.template.response import TemplateResponse + from feincms.content.application.models import UnpackTemplateResponse + from feincms.views.decorators import unpack + + @unpack + def app_detail(request, ...): + return TemplateResponse(request, 'template.html', {...}) + + # or + + def app_detail(request, ...): + return UnpackTemplateResponse(request, 'template.html', {...}) + +The response class can also be easily used with Django's class-based views:: + + class MyListView(generic.ListView): + response_class = UnpackTemplateResponse + +This mechanism supersedes returning a tuple of ``(template_name, context)``. +This is still supported, but lacks the possibility to override HTTP response +headers. + + +Explicit definition of navigation extensions is now possible +============================================================ + +The auto-discovery of navigation extensions always was fragile and had to +happen before the navigation extension itself was registered with the page +class. This has been fixed; it's now possible to explicitly define the list +of navigation extensions which should be available:: + + from feincms.module.page.extensions import navigation + from feincms.module.page.models import Page + from myapp.navigation_extensions import MyappNavigationExtension + + class NavigationExtension(navigation.Extension): + navigation_extensions = [ + MyappNavigationExtension, + ] + + Page.register_extensions( + NavigationExtension, + ) + +The previous method has been deprecated and will stop working in future +versions of FeinCMS. + + +Backwards-incompatible changes +============================== + +* FeinCMS requires a minimum of Django 1.6. + +* The example project has been removed, because it did not really demonstrate + a best practices FeinCMS setup. A standard installation of FeinCMS will + often include additional libraries such as + `feincms-oembed `_, + `form-designer `_ and additional + modules. + + +Removal of deprecated features +------------------------------ + +There were no deprecated features to be removed. + + +New deprecations +================ + +* ``RSSContent`` and ``update_rsscontent`` have been deprecated, those being + the only reason why ``FeinCMS`` depends on ``feedparser``. This will allow + us to remove this dependency. Users should switch to + `feincms-syndication `_ + instead. + +* The automatic discovery of subclasses of ``NavigationExtension`` has been + replaced with an explicit mechanism of defining navigation extensions. + +* ``Page.cache_key`` has never been used by FeinCMS itself and will therefore + be removed in a future release. Comparable functionality has been available + for a long time with ``Page.path_to_cache_key``. + + +Notable features and improvements +================================= + +* Fix the inconsistent filtering of pages inside ``feincms_nav``. Navigation + extensions always came last, but the last release of FeinCMS added navigation + group filtering afterwards. This has been fixed. The workaround for the + previous behavior was to add the matching navigation group to page pretenders + as well. + +* Support for importing PIL as `import Image` has been removed. + +* The builtin and mostly broken frontend editing support has been removed. This + is not a decision against frontend editing / on site editing in general, it + is more about creating a space for new ideas and new implementations. + +* The home-grown schema checking support has been removed. Real migrations + should be used instead. + +* We are logging more stuff. + +* The admin CSS has been updated in preparation for Django's (hopefully!) + upcoming + `django-flat-theme `_ merge. + + +Bugfixes +======== + +* ``{% feincms_nav %}`` now filters by navigation group before applying + navigation extensions for internal consistency. + +* ``{% page_is_active %}`` correctly handles page pretenders now. + + +Compatibility with Django and other apps +======================================== + +FeinCMS 1.11 requires Django 1.6 or better. diff --git a/docs/releases/1.12.rst b/docs/releases/1.12.rst new file mode 100644 index 000000000..4b41d3506 --- /dev/null +++ b/docs/releases/1.12.rst @@ -0,0 +1,118 @@ +========================== +FeinCMS 1.12 release notes +========================== + +Welcome to FeinCMS 1.12! + +.. warning:: + + This is a cleanup release. Lots of changes ahead! Please report problems + in our issue tracker on Github_! + +.. _Github: https://github.com/feincms/feincms/issues + + +Template content requires explicit list of templates +==================================================== + +The template refactor in Django removed the ability to enumerate +templates in template folders. Because of that templates must now +be explicitly specified when creating the content type:: + + Page.create_content_type(TemplateContent, TEMPLATES=[ + ('content/template/something1.html', 'something'), + ('content/template/something2.html', 'something else'), + ('base.html', 'makes no sense'), + ]) + +Also, you need to add a model migration which renames the old +``filename`` field to the new ``template`` field and prepends +``content/template/`` to all filenames:: + + # -*- coding: utf-8 -*- + from __future__ import unicode_literals + + from django.db import models, migrations + + + class Migration(migrations.Migration): + + dependencies = [ + ('page', 'WHATEVER IS APPROPRIATE'), + ] + + operations = [ + migrations.RenameField('TemplateContent', 'filename', 'template'), + migrations.RunSQL( + "UPDATE page_page_templatecontent" + " SET template='content/template/' || template;", + "UPDATE page_page_templatecontent" + " SET template=REPLACE(template, 'content/template/', '');" + ), + ] + + +The blog module has been completely removed +============================================ + +If you need a blog, have a look at Elephantblog_ instead. + +.. _Elephantblog: https://github.com/feincms/feincms-elephantblog + + +Caching of pages in various page manager methods has been removed +================================================================= + +Some methods such as `Page.objects.for_request` automatically cached +the page instances. This behavior lead to non-obvious problems and has +therefore been removed. + + +Backwards-incompatible changes +============================== + +* FeinCMS requires Django 1.7 or better. + +* Django has removed comments support a long time ago, which meant + that our bundled comments content in ``feincms.content.comments`` + was broken for some time. It has been completely removed. + +* ``feincms.content.rss`` has been removed, use ``feincms-syndication`` + instead. + + +Removal of deprecated features +------------------------------ + +South is not supported anymore? Django 1.7 and better only? + +``feincms.views.cbv`` has been removed. Use ``feincms.urls`` and +``feincms.views`` directly instead. + + +New deprecations +================ + +* None. + + +Notable features and improvements +================================= + +* Rich text cleaning using Tidy has been removed. + +* ``FEINCMS_JQUERY_NO_CONFLICT`` is gone. Either use ``django.jQuery`` or + ``feincms.jQuery`` explicitly. + +* Some support has been added for ``django-filer``. + +Bugfixes +======== + +* Too many to list. + + +Compatibility with Django and other apps +======================================== + +FeinCMS 1.12 requires Django 1.7 or better. diff --git a/docs/releases/1.13.rst b/docs/releases/1.13.rst new file mode 100644 index 000000000..a768743ce --- /dev/null +++ b/docs/releases/1.13.rst @@ -0,0 +1,51 @@ +========================== +FeinCMS 1.13 release notes +========================== + +Welcome to FeinCMS 1.13! + + +Compatibility with Django 1.10 +============================== + +The biggest feature of FeinCMS 1.13 is being compatible with Django 1.10 +and Django 1.11. + + +Backwards-incompatible changes +============================== + +* None. + + +Removal of deprecated features +------------------------------ + +* None. + + +New deprecations +================ + +* None. + + +Notable features and improvements +================================= + +* Some support has been added for ``django-filer``. + + +Bugfixes +======== + +* Too many to list. + + +Compatibility with Django and other apps +======================================== + +FeinCMS 1.13 requires Django 1.7 or better. + + +.. _django-mptt: https://github.com/django-mptt/django-mptt diff --git a/docs/settings.rst b/docs/settings.rst new file mode 100644 index 000000000..bc4f037ed --- /dev/null +++ b/docs/settings.rst @@ -0,0 +1,118 @@ +.. _settings: + +======== +Settings +======== + +FeinCMS has a few installation-wide settings which you might want to customize. + +The default settings can be found inside :mod:`feincms.default_settings`. +FeinCMS reads the settings from :mod:`feincms.settings` -- values should be +overridden by placing them in your project's settings. + + +Media library settings +====================== + +``FEINCMS_MEDIALIBRARY_UPLOAD_TO``: Defaults to ``medialibrary/%Y/%m``. Defines +the location of newly uploaded media files. + +``FEINCMS_MEDIALIBRARY_THUMBNAIL``: Defaults to +``feincms.module.medialibrary.thumbnail.default_admin_thumbnail``. The path to +a function which should return the URL to a thumbnail or ``None`` for the +mediafile instance passed as first argument. + +``FEINCMS_MEDIAFILE_OVERWRITE``: Defaults to ``False``. Set this to ``True`` +if uploads should replace previous files using the same path if possible. This +allows copy-pasted paths to work, but prevents using far future expiry headers +for media files. Also, it might not work with all storage backends. + + +Rich text settings +================== + +``FEINCMS_RICHTEXT_INIT_TEMPLATE``: Defaults to +``admin/content/richtext/init_tinymce4.html``. The template which contains the +initialization snippet for the rich text editor. Bundled templates are: + +* ``admin/content/richtext/init_tinymce.html`` for TinyMCE 3.x. +* ``admin/content/richtext/init_tinymce4.html`` for TinyMCE 4.x. +* ``admin/content/richtext/init_tinymce7.html`` for TinyMCE 7.x. +* ``admin/content/richtext/init_ckeditor.html`` for CKEditor. + +``FEINCMS_RICHTEXT_INIT_CONTEXT``: Defaults to +``{'TINYMCE_JS_URL': 'https://cdnjs.cloudflare.com/ajax/libs/tinymce/4.9.11/tinymce.min.js'}``. +A dictionary which is passed to the template mentioned above. Please +refer to the templates directly to see all available variables. + + +Settings for the tree editor +============================ + +``FEINCMS_TREE_EDITOR_INCLUDE_ANCESTORS``: Defaults to ``False``. When this +setting is ``True``, the tree editor shows all objects on a single page, and +also shows all ancestors up to the roots in filtered lists. + + +``FEINCMS_TREE_EDITOR_OBJECT_PERMISSIONS``: Defaults to ``False``. Enables +checking of object level permissions. + + +Settings for the page module +============================ + +``FEINCMS_USE_PAGE_ADMIN``: Defaults to ``True``. The page model admin module +automatically registers the page model with the default admin site if this is +active. Set to ``False`` if you have to configure the page admin module +yourself. + +``FEINCMS_DEFAULT_PAGE_MODEL``: Defaults to ``page.Page``. The page model used +by :mod:`feincms.module.page`. + +``FEINCMS_ALLOW_EXTRA_PATH``: Defaults to ``False``. Activate this to allow +random gunk after a valid page URL. The standard behavior is to raise a 404 +if extra path elements aren't handled by a content type's ``process()`` method. + +``FEINCMS_TRANSLATION_POLICY``: Defaults to ``STANDARD``. How to switch +languages. + +* ``'STANDARD'``: The page a user navigates to sets the site's language + and overwrites whatever was set before. +* ``'EXPLICIT'``: The language set has priority, may only be overridden + by explicitely a language with ``?set_language=xx``. + +``FEINCMS_FRONTEND_LANGUAGES``: Defaults to None; set it to a list of allowed +language codes in the front end so to allow additional languages in the admin +back end for preparing those pages while not yet making the available to the +public. + +``FEINCMS_CMS_404_PAGE``: Defaults to ``None``. Set this if you want the page +handling mechanism to try and find a CMS page with that path if it encounters +a page not found situation. + +``FEINCMS_SINGLETON_TEMPLATE_CHANGE_ALLOWED``: Defaults to ``False``. Prevent +changing template within admin for pages which have been allocated a Template +with ``singleton=True`` -- template field will become read-only for singleton +pages. + +``FEINCMS_SINGLETON_TEMPLATE_DELETION_ALLOWED``: Defaults to ``False``. +Prevent admin page deletion for pages which have been allocated a Template with +``singleton=True``. + +``FEINCMS_MEDIAFILE_TRANSLATIONS``: Defaults to ``True``. Set to ``False`` if +you want FeinCMS to not translate ``MediaFile`` names, and instead just use the +filename directly. + + +Various settings +================ + +``FEINCMS_THUMBNAIL_DIR``: Defaults to ``_thumbs/``. Defines a prefix for media +file thumbnails. This allows you to easily remove all thumbnails without fear +of removing files belonging to image and file fields. + +``FEINCMS_THUMBNAIL_CACHE_TIMEOUT``: ``feincms_thumbnail`` template +filter library cache timeout. The default is to not cache anything for +backwards compatibility. If you use cloud storage AND +``feincms_thumbnail`` it is recommended to set the timeout to a large +value. diff --git a/docs/templatetags.rst b/docs/templatetags.rst index 203168b87..42bb13e5b 100644 --- a/docs/templatetags.rst +++ b/docs/templatetags.rst @@ -54,9 +54,6 @@ the ``feincms_tags`` template tag library:: context = kwargs.get('context') -.. function:: feincms_frontend_editing: - - Page module-specific template tags ================================== @@ -72,6 +69,7 @@ All page module-specific template tags are contained in ``feincms_page_tags``:: level: 1 = toplevel, 2 = sublevel, 3 = sub-sublevel depth: 1 = only one level, 2 = subpages too + group: Only used with the ``navigationgroups`` extension If you set depth to something else than 1, you might want to look into the ``tree_info`` template tag from the mptt_tags library. @@ -85,6 +83,16 @@ All page module-specific template tags are contained in ``feincms_page_tags``:: {{ p.title }} {% endfor %} + Example for outputting only the footer navigation when using the + default configuration of the ``navigationgroups`` page extension:: + + {% load feincms_page_tags %} + + {% feincms_nav feincms_page level=2 depth=1 group='footer' as meta %} + {% for p in sublevel %} + {{ p.title }} + {% endfor %} + .. function:: siblings_along_path_to: This is a filter designed to work in close conjunction with the diff --git a/docs/versioning.rst b/docs/versioning.rst index 462ad6ff6..b4374c9f1 100644 --- a/docs/versioning.rst +++ b/docs/versioning.rst @@ -12,8 +12,12 @@ with django-reversion_: * Add ``'reversion'`` to the list of installed applications. * Add ``'reversion.middleware.RevisionMiddleware'`` to ``MIDDLEWARE_CLASSES``. -* Call ``Page.register_with_reversion()`` after all content types have been - created (after all ``create_content_type`` invocations). +* Call ``Page.register_with_reversion(**kwargs)`` after all content types have been + created (after all ``create_content_type`` invocations). You can optionally + supply kwargs_ that will be passed to ``reversion.register()``. +* Add ``FEINCMS_USE_PAGE_ADMIN = False`` to your ``settings`` file. + +.. _kwargs: https://django-reversion.readthedocs.io/en/stable/api.html#registration-api Now, you need to create your own model admin subclass inheriting from both FeinCMS' ``PageAdmin`` and from reversions ``VersionAdmin``:: @@ -23,8 +27,6 @@ FeinCMS' ``PageAdmin`` and from reversions ``VersionAdmin``:: from feincms.module.page.modeladmins import PageAdmin from reversion.admin import VersionAdmin - admin.site.unregister(Page) - class VersionedPageAdmin(PageAdmin, VersionAdmin): pass @@ -46,3 +48,11 @@ Finally, you should ensure that initial revisions are created using contains an up-to-date list of compatible releases. The reversion support in FeinCMS requires at least django-reversion 1.6. + +.. note:: + + Only the Page module is versioned. MediaFiles are not. If a user deletes an + image from the MediaLibrary by accident it cannot be recovered. + Furthermore, if the position of a page has been moved within the tree, + the recovery will break the tree structure. + You can try to fix it using the ``rebuild_mptt`` command. diff --git a/example/README b/example/README deleted file mode 100644 index 4b33124e6..000000000 --- a/example/README +++ /dev/null @@ -1,3 +0,0 @@ -This is a really basic example how to use FeinCMS. - -Username/Password for the admin interface are admin and password. diff --git a/example/admin.py b/example/admin.py deleted file mode 100644 index 5c92e55d8..000000000 --- a/example/admin.py +++ /dev/null @@ -1,15 +0,0 @@ -from django.contrib import admin - -from feincms.admin import tree_editor - -from example.models import Category - - -class CategoryAdmin(tree_editor.TreeEditor): - list_display = ('name', 'slug') - list_filter = ('parent',) - prepopulated_fields = { - 'slug': ('name',), - } - -admin.site.register(Category, CategoryAdmin) diff --git a/example/blog_urls.py b/example/blog_urls.py deleted file mode 100644 index 9aae16176..000000000 --- a/example/blog_urls.py +++ /dev/null @@ -1,15 +0,0 @@ -from django.conf.urls import patterns, url -from django.views import generic - -from feincms.module.blog.models import Entry - - -urlpatterns = patterns( - '', - url(r'^(?P\d+)/', generic.DetailView.as_view( - queryset=Entry.objects.all(), - ), name='blog_entry_detail'), - url(r'^$', generic.ListView.as_view( - queryset=Entry.objects.all(), - ), name='blog_entry_list'), -) diff --git a/example/example.db b/example/example.db deleted file mode 100644 index 1b42d9f24..000000000 Binary files a/example/example.db and /dev/null differ diff --git a/example/manage.py b/example/manage.py deleted file mode 100755 index 729887ecd..000000000 --- a/example/manage.py +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/env python -import os -import sys - -sys.path.insert( - 0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) - -os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'example.settings') - -from django.core.management import execute_from_command_line - -execute_from_command_line(sys.argv) diff --git a/example/management/commands/generate_big_tree.py b/example/management/commands/generate_big_tree.py deleted file mode 100644 index c3e6ddf90..000000000 --- a/example/management/commands/generate_big_tree.py +++ /dev/null @@ -1,45 +0,0 @@ -# ------------------------------------------------------------------------ -# coding=utf-8 -# ------------------------------------------------------------------------ - -from __future__ import print_function - -from django.core.management.base import NoArgsCommand - -from feincms.module.page.models import Page - - -class Command(NoArgsCommand): - help = ( - "Run this command to generate a big tree for performance testing" - " purposes.") - - def handle_noargs(self, **options): - parents = [None] * 5 - - Page.objects.all().delete() - - for i1 in range(5): - parents[0] = Page.objects.create( - title='Page %s' % (i1,), - ) - - for i2 in range(5): - parents[1] = Page.objects.create( - title='Page %s.%s' % (i1, i2), - parent=parents[0], - ) - - for i3 in range(5): - parents[2] = Page.objects.create( - title='Page %s.%s.%s' % (i1, i2, i3), - parent=parents[1], - ) - - for i4 in range(5): - parents[3] = Page.objects.create( - title='Page %s.%s.%s.%s' % (i1, i2, i3, i4), - parent=parents[2], - ) - - print(parents) diff --git a/example/media/.gitignore b/example/media/.gitignore deleted file mode 100644 index 72e8ffc0d..000000000 --- a/example/media/.gitignore +++ /dev/null @@ -1 +0,0 @@ -* diff --git a/example/models.py b/example/models.py deleted file mode 100644 index 758a0d125..000000000 --- a/example/models.py +++ /dev/null @@ -1,120 +0,0 @@ -from django import forms -from django.db import models -from django.utils.encoding import python_2_unicode_compatible -from django.utils.text import capfirst -from django.utils.translation import ugettext_lazy as _ - -from mptt.models import MPTTModel - -from feincms.module.blog.models import Entry, EntryAdmin -from feincms.module.page.models import Page -from feincms.content.raw.models import RawContent -from feincms.content.image.models import ImageContent -from feincms.content.medialibrary.models import MediaFileContent -from feincms.content.application.models import ApplicationContent -from feincms.module.page.extensions.navigation import ( - NavigationExtension, PagePretender) -from feincms.content.application.models import app_reverse - - -Page.register_templates({ - 'key': 'base', - 'title': 'Base Template', - 'path': 'base.html', - 'regions': ( - ('main', 'Main region'), - ('sidebar', 'Sidebar', 'inherited'), - ), -}) -Page.create_content_type(RawContent) -Page.create_content_type( - MediaFileContent, - TYPE_CHOICES=( - ('default', 'Default position'), - ), -) -Page.create_content_type( - ImageContent, - POSITION_CHOICES=( - ('default', 'Default position'), - ), -) - - -def get_admin_fields(form, *args, **kwargs): - return { - 'exclusive_subpages': forms.BooleanField( - label=capfirst(_('exclusive subpages')), - required=False, - initial=form.instance.parameters.get('exclusive_subpages', False), - help_text=_( - 'Exclude everything other than the application\'s' - ' content when rendering subpages.'), - ), - } - - -Page.create_content_type(ApplicationContent, APPLICATIONS=( - ('blog_urls', 'Blog', { - 'admin_fields': get_admin_fields, - 'urls': 'example.blog_urls', - }), -)) - - -Entry.register_regions( - ('main', 'Main region'), -) -Entry.create_content_type(RawContent) -Entry.create_content_type( - ImageContent, - POSITION_CHOICES=( - ('default', 'Default position'), - ) -) - - -class BlogEntriesNavigationExtension(NavigationExtension): - """ - Extended navigation for blog entries. - - It would be added to 'Blog' page properties in admin. - """ - name = _('all blog entries') - - def children(self, page, **kwargs): - for entry in Entry.objects.all(): - yield PagePretender( - title=entry.title, - url=app_reverse( - 'blog_entry_detail', 'blog_urls', kwargs={'pk': entry.id}), - level=page.level + 1, - ) - -Page.register_extensions( - 'feincms.module.page.extensions.navigation', - 'feincms.module.page.extensions.sites', -) - - -@python_2_unicode_compatible -class Category(MPTTModel): - name = models.CharField(max_length=20) - slug = models.SlugField() - parent = models.ForeignKey( - 'self', blank=True, null=True, related_name='children') - - class Meta: - ordering = ['tree_id', 'lft'] - verbose_name = 'category' - verbose_name_plural = 'categories' - - def __str__(self): - return self.name - - -# add m2m field to entry so it shows up in entry admin -Entry.add_to_class( - 'categories', - models.ManyToManyField(Category, blank=True, null=True)) -EntryAdmin.list_filter += ('categories',) diff --git a/example/settings.py b/example/settings.py deleted file mode 100644 index b1f2d9c2b..000000000 --- a/example/settings.py +++ /dev/null @@ -1,95 +0,0 @@ -# Django settings for example project. - -import os - -DEBUG = True -TEMPLATE_DEBUG = DEBUG - -ADMINS = ( - # ('Your Name', 'your_email@domain.com'), -) - -MANAGERS = ADMINS - -DATABASE_ENGINE = 'sqlite3' -DATABASE_NAME = os.path.join(os.path.dirname(__file__), 'example.db') - -DATABASES = {'default': { - 'ENGINE': 'django.db.backends.sqlite3', - 'NAME': DATABASE_NAME, -}} - -TIME_ZONE = 'America/Chicago' - -LANGUAGE_CODE = 'en-us' - -SITE_ID = int(os.environ.get('SITE_ID', 1)) - -USE_I18N = True - -MEDIA_ROOT = os.path.join(os.path.dirname(__file__), 'media/') -MEDIA_URL = '/media/' -STATIC_ROOT = os.path.join(os.path.dirname(__file__), 'static/') -STATIC_URL = '/static/' - -SECRET_KEY = '_wn95s-apfd-442cby5m^_^ak6+5(fyn3lvnvtn7!si&o)1x^w' - -TEMPLATE_CONTEXT_PROCESSORS = ( - 'django.contrib.auth.context_processors.auth', - 'django.core.context_processors.debug', - 'django.core.context_processors.i18n', - 'django.core.context_processors.media', - 'django.core.context_processors.request', - 'django.core.context_processors.static', - - 'feincms.context_processors.add_page_if_missing', -) - -TEMPLATE_LOADERS = ( - 'django.template.loaders.filesystem.Loader', - 'django.template.loaders.app_directories.Loader', -) - -MIDDLEWARE_CLASSES = ( - 'django.middleware.common.CommonMiddleware', - 'django.contrib.sessions.middleware.SessionMiddleware', - 'django.middleware.csrf.CsrfViewMiddleware', - 'django.contrib.auth.middleware.AuthenticationMiddleware', - 'django.contrib.messages.middleware.MessageMiddleware', -) - -ROOT_URLCONF = 'example.urls' - -TEMPLATE_DIRS = ( -) - -INSTALLED_APPS = ( - 'django.contrib.auth', - 'django.contrib.contenttypes', - 'django.contrib.sessions', - 'django.contrib.sites', - 'django.contrib.admin', - 'django.contrib.staticfiles', - - 'feincms', - 'feincms.module.blog', - 'feincms.module.page', - 'feincms.module.medialibrary', - 'example', - - 'mptt', -) - -LANGUAGES = ( - ('en', 'English'), - ('de', 'German'), -) - -FEINCMS_TREE_EDITOR_INCLUDE_ANCESTORS = True - -from feincms.content.application.models import app_reverse -ABSOLUTE_URL_OVERRIDES = { - 'blog.entry': lambda entry: app_reverse( - 'blog_entry_detail', 'blog_urls', - args=(entry.id,)), -} diff --git a/example/templates/404.html b/example/templates/404.html deleted file mode 100644 index 81b0d6084..000000000 --- a/example/templates/404.html +++ /dev/null @@ -1 +0,0 @@ -

Page not found

diff --git a/example/templates/base.html b/example/templates/base.html deleted file mode 100644 index 4e43966a3..000000000 --- a/example/templates/base.html +++ /dev/null @@ -1,118 +0,0 @@ -{% load applicationcontent_tags feincms_tags feincms_page_tags %} - - - {{ feincms_page.title }} - - - -

{{ feincms_page.title }}

- - - -
-
-

Main content

- {% block content %}{% feincms_render_region feincms_page "main" request %}{% endblock %} -
- - -
- - {% feincms_frontend_editing feincms_page request %} - - {% get_fragment request "something" %} - - diff --git a/example/templates/blog/entry_detail.html b/example/templates/blog/entry_detail.html deleted file mode 100644 index 4903fdf66..000000000 --- a/example/templates/blog/entry_detail.html +++ /dev/null @@ -1,11 +0,0 @@ -{% load i18n %} -{% load feincms_tags applicationcontent_tags %} - -
-

{{object.title}}

- {% feincms_render_region object "main" request %} -
- -

- {% trans "All entries" %} -

diff --git a/example/templates/blog/entry_list.html b/example/templates/blog/entry_list.html deleted file mode 100644 index 8a6d2b49a..000000000 --- a/example/templates/blog/entry_list.html +++ /dev/null @@ -1,5 +0,0 @@ -{% load applicationcontent_tags %} - -{% for object in object_list %} -

{{ object.title }}

-{% endfor %} diff --git a/example/urls.py b/example/urls.py deleted file mode 100644 index 5cba5b068..000000000 --- a/example/urls.py +++ /dev/null @@ -1,16 +0,0 @@ -import os - -from django.conf.urls import patterns, include, url -from django.contrib import admin -from django.contrib.staticfiles.urls import staticfiles_urlpatterns - -admin.autodiscover() - -urlpatterns = patterns( - '', - url(r'^admin/', include(admin.site.urls)), - url(r'^media/(?P.*)$', 'django.views.static.serve', { - 'document_root': os.path.join(os.path.dirname(__file__), 'media/')}), - url(r'', include('feincms.contrib.preview.urls')), - url(r'', include('feincms.urls')) -) + staticfiles_urlpatterns() diff --git a/feincms/__init__.py b/feincms/__init__.py index 6a8659b08..8cd0f3219 100644 --- a/feincms/__init__.py +++ b/feincms/__init__.py @@ -1,16 +1,15 @@ -from __future__ import absolute_import, unicode_literals +VERSION = (25, 5, 1) +__version__ = ".".join(map(str, VERSION)) -VERSION = (1, 9, 5) -__version__ = '.'.join(map(str, VERSION)) - -class LazySettings(object): +class LazySettings: def _load_settings(self): - from feincms import default_settings from django.conf import settings as django_settings + from feincms import default_settings + for key in dir(default_settings): - if not key.startswith('FEINCMS_'): + if not key.startswith("FEINCMS_"): continue value = getattr(default_settings, key) @@ -22,68 +21,5 @@ def __getattr__(self, attr): del self.__class__.__getattr__ return self.__dict__[attr] -settings = LazySettings() - - -COMPLETELY_LOADED = False - -def ensure_completely_loaded(force=False): - """ - This method ensures all models are completely loaded - - FeinCMS requires Django to be completely initialized before proceeding, - because of the extension mechanism and the dynamically created content - types. - - For more informations, have a look at issue #23 on github: - http://github.com/feincms/feincms/issues#issue/23 - """ - - global COMPLETELY_LOADED - if COMPLETELY_LOADED and not force: - return True - - # Ensure meta information concerning related fields is up-to-date. - # Upon accessing the related fields information from Model._meta, - # the related fields are cached and never refreshed again (because - # models and model relations are defined upon import time, if you - # do not fumble around with models like we do in FeinCMS.) - # - # Here we flush the caches rather than actually _filling them so - # that relations defined after all content types registrations - # don't miss out. - from django.db.models import loading - for model in loading.get_models(): - for cache_name in ( - '_field_cache', '_field_name_cache', '_m2m_cache', - '_related_objects_cache', '_related_many_to_many_cache', - '_name_map'): - try: - delattr(model._meta, cache_name) - except AttributeError: - pass - - # Randomly call some cache filling methods - # http://goo.gl/XNI2qz - model._meta._fill_fields_cache() - - # Calls to get_models(...) are cached by the arguments used in the call. - # This cache is normally cleared in loading.register_models(), but we - # invalidate the get_models() cache, by calling get_models - # above before all apps have loaded. (Django's load_app() doesn't clear the - # get_models cache as it perhaps should). So instead we clear the - # get_models cache again here. If we don't do this, Django 1.5 chokes on - # a model validation error (Django 1.4 doesn't exhibit this problem). - # See Issue #323 on github. - if hasattr(loading, 'cache'): - loading.cache._get_models_cache.clear() - - if hasattr(loading.app_cache_ready, '__call__'): - if loading.app_cache_ready(): - COMPLETELY_LOADED = True - else: - # TODO Django 1.7 offers us better ways of handling this, maybe. - if loading.app_cache_ready: - COMPLETELY_LOADED = True - return True +settings = LazySettings() diff --git a/feincms/_internal.py b/feincms/_internal.py index 5109a9d32..ab72b9e17 100644 --- a/feincms/_internal.py +++ b/feincms/_internal.py @@ -4,7 +4,7 @@ http://mail.python.org/pipermail/python-dev/2008-January/076194.html """ -from __future__ import absolute_import, unicode_literals +__all__ = ("monkeypatch_method", "monkeypatch_property") def monkeypatch_method(cls): @@ -19,6 +19,7 @@ def (self, [...]): def decorator(func): setattr(cls, func.__name__, func) return func + return decorator @@ -34,24 +35,5 @@ def (self, [...]): def decorator(func): setattr(cls, func.__name__, property(func)) return func - return decorator - -def monkeypatch_class(name, bases, namespace): - """ - A metaclass to add a number of methods (or other attributes) to an - existing class, using a convenient class notation:: - - class (): - __metaclass__ = monkeypatch_class - def (...): ... - def (...): ... - ... - """ - - assert len(bases) == 1, "Exactly one base class required" - base = bases[0] - for name, value in namespace.iteritems(): - if name != "__metaclass__": - setattr(base, name, value) - return base + return decorator diff --git a/feincms/admin/__init__.py b/feincms/admin/__init__.py index e69de29bb..bb45c8662 100644 --- a/feincms/admin/__init__.py +++ b/feincms/admin/__init__.py @@ -0,0 +1,15 @@ +from django.contrib.admin.filters import FieldListFilter + +from .filters import CategoryFieldListFilter, ParentFieldListFilter + + +FieldListFilter.register( + lambda f: getattr(f, "parent_filter", False), + ParentFieldListFilter, + take_priority=True, +) +FieldListFilter.register( + lambda f: getattr(f, "category_filter", False), + CategoryFieldListFilter, + take_priority=True, +) diff --git a/feincms/admin/filters.py b/feincms/admin/filters.py new file mode 100644 index 000000000..7ae5bdc5a --- /dev/null +++ b/feincms/admin/filters.py @@ -0,0 +1,120 @@ +# Thanks to http://www.djangosnippets.org/snippets/1051/ +# +# Authors: Marinho Brandao +# Guilherme M. Gondim (semente) + + +from operator import itemgetter + +import django +from django.contrib.admin.filters import ChoicesFieldListFilter +from django.db.models import Count +from django.utils.encoding import smart_str +from django.utils.safestring import mark_safe +from django.utils.translation import gettext as _ + +from feincms.utils import shorten_string + + +class ParentFieldListFilter(ChoicesFieldListFilter): + """ + Improved list_filter display for parent Pages by nicely indenting hierarchy + + In theory this would work with any mptt model which uses a "title" + attribute. + + my_model_field.page_parent_filter = True + """ + + def __init__(self, field, request, params, model, model_admin, field_path=None): + super().__init__(field, request, params, model, model_admin, field_path) + + parent_ids = ( + model.objects.exclude(parent=None) + .values_list("parent__id", flat=True) + .order_by("parent__id") + .distinct() + ) + parents = model.objects.filter(pk__in=parent_ids).values_list( + "pk", "title", "level" + ) + self.lookup_choices = [ + ( + pk, + "{}{}".format( + "  " * level, shorten_string(title, max_length=25) + ), + ) + for pk, title, level in parents + ] + + def choices(self, changelist): + yield { + "selected": self.lookup_val is None, + "query_string": changelist.get_query_string({}, [self.lookup_kwarg]), + "display": _("All"), + } + + # Pre Django 5 lookup_val would be a scalar, now it can do multiple + # selections and thus is a list. Deal with that. + lookup_vals = self.lookup_val + if lookup_vals is not None and django.VERSION < (5,): + lookup_vals = [lookup_vals] + + for pk, title in self.lookup_choices: + yield { + "selected": lookup_vals is not None and str(pk) in lookup_vals, + "query_string": changelist.get_query_string({self.lookup_kwarg: pk}), + "display": mark_safe(smart_str(title)), + } + + def title(self): + return _("Parent") + + +class CategoryFieldListFilter(ChoicesFieldListFilter): + """ + Customization of ChoicesFilterSpec which sorts in the user-expected format + + my_model_field.category_filter = True + """ + + def __init__(self, field, *args, **kwargs): + super().__init__(field, *args, **kwargs) + + # Restrict results to categories which are actually in use: + related_model = field.remote_field.model + related_name = field.related_query_name() + + self.lookup_choices = sorted( + ( + (i.pk, f"{i} ({i._related_count})") + for i in related_model.objects.annotate( + _related_count=Count(related_name) + ).exclude(_related_count=0) + ), + key=itemgetter(1), + ) + + def choices(self, changelist): + yield { + "selected": self.lookup_val is None, + "query_string": changelist.get_query_string({}, [self.lookup_kwarg]), + "display": _("All"), + } + + # Pre Django 5 lookup_val would be a scalar, now it can do multiple + # selections and thus is a list. Deal with that. + lookup_vals = self.lookup_val + if lookup_vals is not None and django.VERSION < (5,): + lookup_vals = [lookup_vals] + + for pk, title in self.lookup_choices: + yield { + "selected": lookup_vals is not None and str(pk) in lookup_vals, + "query_string": changelist.get_query_string({self.lookup_kwarg: pk}), + "display": mark_safe(smart_str(title)), + } + + def title(self): + return _("Category") diff --git a/feincms/admin/filterspecs.py b/feincms/admin/filterspecs.py deleted file mode 100644 index f189927f1..000000000 --- a/feincms/admin/filterspecs.py +++ /dev/null @@ -1,107 +0,0 @@ -# encoding=utf-8 -# Thanks to http://www.djangosnippets.org/snippets/1051/ -# -# Authors: Marinho Brandao -# Guilherme M. Gondim (semente) - -from __future__ import absolute_import, unicode_literals - -from django.contrib.admin.filters import ( - FieldListFilter, ChoicesFieldListFilter) -from django.utils import six -from django.utils.encoding import smart_text -from django.utils.safestring import mark_safe -from django.utils.translation import ugettext as _ - -from feincms.utils import shorten_string - - -class ParentFieldListFilter(ChoicesFieldListFilter): - """ - Improved list_filter display for parent Pages by nicely indenting hierarchy - - In theory this would work with any mptt model which uses a "title" - attribute. - - my_model_field.page_parent_filter = True - """ - - def __init__(self, f, request, params, model, model_admin, - field_path=None): - super(ParentFieldListFilter, self).__init__( - f, request, params, model, model_admin, field_path) - - parent_ids = model.objects.exclude(parent=None).values_list( - "parent__id", flat=True).order_by("parent__id").distinct() - parents = model.objects.filter(pk__in=parent_ids).values_list( - "pk", "title", "level") - self.lookup_choices = [( - pk, - "%s%s" % (" " * level, shorten_string(title, max_length=25)), - ) for pk, title, level in parents] - - def choices(self, cl): - yield { - 'selected': self.lookup_val is None, - 'query_string': cl.get_query_string({}, [self.lookup_kwarg]), - 'display': _('All') - } - - for pk, title in self.lookup_choices: - yield { - 'selected': pk == int(self.lookup_val or '0'), - 'query_string': cl.get_query_string({self.lookup_kwarg: pk}), - 'display': mark_safe(smart_text(title)) - } - - def title(self): - return _('Parent') - - -class CategoryFieldListFilter(ChoicesFieldListFilter): - """ - Customization of ChoicesFilterSpec which sorts in the user-expected format - - my_model_field.category_filter = True - """ - - def __init__(self, f, request, params, model, model_admin, - field_path=None): - super(CategoryFieldListFilter, self).__init__( - f, request, params, model, model_admin, field_path) - - # Restrict results to categories which are actually in use: - self.lookup_choices = [( - i.pk, - six.text_type(i)) - for i in f.related.parent_model.objects.exclude( - **{f.related.var_name: None}) - ] - self.lookup_choices.sort(key=lambda i: i[1]) - - def choices(self, cl): - yield { - 'selected': self.lookup_val is None, - 'query_string': cl.get_query_string({}, [self.lookup_kwarg]), - 'display': _('All') - } - - for pk, title in self.lookup_choices: - yield { - 'selected': pk == int(self.lookup_val or '0'), - 'query_string': cl.get_query_string({self.lookup_kwarg: pk}), - 'display': mark_safe(smart_text(title)) - } - - def title(self): - return _('Category') - - -FieldListFilter.register( - lambda f: getattr(f, 'parent_filter', False), - ParentFieldListFilter, - take_priority=True) -FieldListFilter.register( - lambda f: getattr(f, 'category_filter', False), - CategoryFieldListFilter, - take_priority=True) diff --git a/feincms/admin/item_editor.py b/feincms/admin/item_editor.py index 731512029..8638c9089 100644 --- a/feincms/admin/item_editor.py +++ b/feincms/admin/item_editor.py @@ -1,35 +1,24 @@ # ------------------------------------------------------------------------ -# coding=utf-8 # ------------------------------------------------------------------------ -from __future__ import absolute_import, unicode_literals import copy import logging -import re import warnings -from django import forms, template +from django import forms from django.contrib.admin.options import InlineModelAdmin -from django.contrib.admin.util import unquote -from django.db.models import loading -from django.forms.models import modelform_factory +from django.contrib.admin.utils import unquote +from django.contrib.auth import get_permission_codename from django.http import Http404 -from django.shortcuts import render_to_response -from django.utils.encoding import force_text -from django.utils.functional import curry -from django.utils.translation import ugettext as _ -from feincms import settings, ensure_completely_loaded from feincms.extensions import ExtensionModelAdmin from feincms.signals import itemeditor_post_save_related -from feincms.templatetags.feincms_admin_tags import is_popup_var # ------------------------------------------------------------------------ -FRONTEND_EDITING_MATCHER = re.compile(r'(\d+)\|(\w+)\|(\d+)') -FEINCMS_CONTENT_FIELDSET_NAME = 'FEINCMS_CONTENT' -FEINCMS_CONTENT_FIELDSET = (FEINCMS_CONTENT_FIELDSET_NAME, {'fields': ()}) +FEINCMS_CONTENT_FIELDSET_NAME = "FEINCMS_CONTENT" +FEINCMS_CONTENT_FIELDSET = (FEINCMS_CONTENT_FIELDSET_NAME, {"fields": ()}) logger = logging.getLogger(__name__) @@ -53,8 +42,8 @@ class FeinCMSInline(InlineModelAdmin): form = ItemEditorForm extra = 0 - fk_name = 'parent' - template = 'admin/feincms/content_inline.html' + fk_name = "parent" + template = "admin/feincms/content_inline.html" # ------------------------------------------------------------------------ @@ -68,14 +57,8 @@ class ItemEditor(ExtensionModelAdmin): the standard ``ModelAdmin`` class. """ - def __init__(self, model, admin_site): - ensure_completely_loaded() - - super(ItemEditor, self).__init__(model, admin_site) - def get_inline_instances(self, request, *args, **kwargs): - inline_instances = super(ItemEditor, self).get_inline_instances( - request, *args, **kwargs) + inline_instances = super().get_inline_instances(request, *args, **kwargs) self.append_feincms_inlines(inline_instances, request) return inline_instances @@ -88,13 +71,16 @@ def append_feincms_inlines(self, inline_instances, request): inline_instances.append(inline_instance) def can_add_content(self, request, content_type): - perm = '.'.join(( - content_type._meta.app_label, - content_type._meta.get_add_permission())) + perm = ".".join( + ( + content_type._meta.app_label, + get_permission_codename("add", content_type._meta), + ) + ) return request.user.has_perm(perm) def get_feincms_inlines(self, model, request): - """ Generate genuine django inlines for registered content types. """ + """Generate genuine django inlines for registered content types.""" model._needs_content_types() inlines = [] @@ -102,126 +88,50 @@ def get_feincms_inlines(self, model, request): if not self.can_add_content(request, content_type): continue - attrs = { - '__module__': model.__module__, - 'model': content_type, - } + attrs = {"__module__": model.__module__, "model": content_type} - if hasattr(content_type, 'feincms_item_editor_inline'): + if hasattr(content_type, "feincms_item_editor_inline"): inline = content_type.feincms_item_editor_inline - attrs['form'] = inline.form + attrs["form"] = inline.form - if hasattr(content_type, 'feincms_item_editor_form'): + if hasattr(content_type, "feincms_item_editor_form"): warnings.warn( - 'feincms_item_editor_form on %s is ignored because ' - 'feincms_item_editor_inline is set too' % content_type, - RuntimeWarning) + "feincms_item_editor_form on %s is ignored because " + "feincms_item_editor_inline is set too" % content_type, + RuntimeWarning, + ) else: inline = FeinCMSInline - attrs['form'] = getattr( - content_type, 'feincms_item_editor_form', inline.form) + attrs["form"] = getattr( + content_type, "feincms_item_editor_form", inline.form + ) - name = '%sFeinCMSInline' % content_type.__name__ + name = "%sFeinCMSInline" % content_type.__name__ # TODO: We generate a new class every time. Is that really wanted? inline_class = type(str(name), (inline,), attrs) inlines.append(inline_class) return inlines - def _frontend_editing_view(self, request, cms_id, content_type, - content_id): - """ - This view is used strictly for frontend editing -- it is not used - inside the standard administration interface. - - The code in feincms/templates/admin/feincms/fe_tools.html knows how to - call this view correctly. - """ - - try: - model_cls = loading.get_model( - self.model._meta.app_label, content_type) - obj = model_cls.objects.get(parent=cms_id, id=content_id) - except: - raise Http404() - - form_class_base = getattr( - model_cls, 'feincms_item_editor_form', ItemEditorForm) - - ModelForm = modelform_factory( - model_cls, - exclude=('parent', 'region', 'ordering'), - form=form_class_base, - formfield_callback=curry( - self.formfield_for_dbfield, request=request)) - - # we do not want to edit these two fields in the frontend editing mode; - # we are strictly editing single content blocks there. We have to - # remove them from the form because we explicitly redefined them in the - # ItemEditorForm definition above. Just using exclude is not enough. - del ModelForm.base_fields['region'] - del ModelForm.base_fields['ordering'] - - if request.method == 'POST': - # The prefix is used to replace the formset identifier from the - # ItemEditor interface. Customization of the form is easily - # possible through either matching the prefix (frontend editing) or - # the formset identifier (ItemEditor) as it is done in the richtext - # and mediafile init.html item editor includes. - form = ModelForm(request.POST, instance=obj, prefix=content_type) - - if form.is_valid(): - obj = form.save() - - return render_to_response( - 'admin/feincms/fe_editor_done.html', { - 'content': obj.render(request=request), - 'identifier': obj.fe_identifier(), - 'FEINCMS_JQUERY_NO_CONFLICT': - settings.FEINCMS_JQUERY_NO_CONFLICT, - }, context_instance=template.RequestContext(request)) - else: - form = ModelForm(instance=obj, prefix=content_type) - - context = self.get_extra_context(request) - context.update({ - 'frontend_editing': True, - 'title': _('Change %s') % force_text(model_cls._meta.verbose_name), - 'object': obj, - 'form': form, - 'is_popup': True, - 'media': self.media, - }) - - return render_to_response( - 'admin/feincms/fe_editor.html', - context, - context_instance=template.RequestContext(request)) - def get_content_type_map(self, request): - """ Prepare mapping of content types to their prettified names. """ + """Prepare mapping of content types to their prettified names.""" content_types = [] for content_type in self.model._feincms_content_types: if self.model == content_type._feincms_content_class: content_name = content_type._meta.verbose_name - content_types.append( - (content_name, content_type.__name__.lower())) + content_types.append((content_name, content_type.__name__.lower())) return content_types def get_extra_context(self, request): - """ Return extra context parameters for add/change views. """ + """Return extra context parameters for add/change views.""" extra_context = { - 'model': self.model, - 'available_templates': getattr( - self.model, '_feincms_templates', ()), - 'has_parent_attribute': hasattr(self.model, 'parent'), - 'content_types': self.get_content_type_map(request), - 'FEINCMS_JQUERY_NO_CONFLICT': settings.FEINCMS_JQUERY_NO_CONFLICT, - 'FEINCMS_CONTENT_FIELDSET_NAME': FEINCMS_CONTENT_FIELDSET_NAME, - - 'FEINCMS_FRONTEND_EDITING': settings.FEINCMS_FRONTEND_EDITING, - 'FEINCMS_POPUP_VAR': is_popup_var(), + "request": request, + "model": self.model, + "available_templates": getattr(self.model, "_feincms_templates", ()), + "has_parent_attribute": hasattr(self.model, "parent"), + "content_types": self.get_content_type_map(request), + "FEINCMS_CONTENT_FIELDSET_NAME": FEINCMS_CONTENT_FIELDSET_NAME, } for processor in self.model.feincms_item_editor_context_processors: @@ -232,9 +142,7 @@ def get_extra_context(self, request): def add_view(self, request, **kwargs): if not self.has_add_permission(request): logger.warning( - "Denied adding %s to \"%s\" (no add permission)", - self.model, - request.user + 'Denied adding %s to "%s" (no add permission)', self.model, request.user ) raise Http404 @@ -242,86 +150,60 @@ def add_view(self, request, **kwargs): # insert dummy object as 'original' so template code can grab defaults # for template, etc. - context['original'] = self.model() + context["original"] = self.model() # If there are errors in the form, we need to preserve the object's # template as it was set when the user attempted to save it, so that # the same regions appear on screen. - if request.method == 'POST' and \ - hasattr(self.model, '_feincms_templates'): - context['original'].template_key = request.POST['template_key'] + if request.method == "POST" and hasattr(self.model, "_feincms_templates"): + context["original"].template_key = request.POST["template_key"] context.update(self.get_extra_context(request)) - context.update(kwargs.get('extra_context', {})) - kwargs['extra_context'] = context - return super(ItemEditor, self).add_view(request, **kwargs) + context.update(kwargs.get("extra_context", {})) + kwargs["extra_context"] = context + return super().add_view(request, **kwargs) def render_change_form(self, request, context, **kwargs): - if kwargs.get('add'): - if request.method == 'GET' and 'adminform' in context: - if 'template_key' in context['adminform'].form.initial: - context['original'].template_key = ( - context['adminform'].form.initial['template_key']) + if kwargs.get("add"): + if request.method == "GET" and "adminform" in context: + if "template_key" in context["adminform"].form.initial: + context["original"].template_key = context[ + "adminform" + ].form.initial["template_key"] # ensure that initially-selected template in form is also # used to render the initial regions in the item editor - return super( - ItemEditor, self).render_change_form(request, context, **kwargs) + return super().render_change_form(request, context, **kwargs) def change_view(self, request, object_id, **kwargs): obj = self.get_object(request, unquote(object_id)) if not self.has_change_permission(request, obj): logger.warning( - "Denied editing %s to \"%s\" (no edit permission)", + 'Denied editing %s to "%s" (no edit permission)', self.model, - request.user + request.user, ) raise Http404 - # Recognize frontend editing requests - # This is done here so that the developer does not need to add - # additional entries to # urls.py or something... - res = FRONTEND_EDITING_MATCHER.search(object_id) - if res: - return self._frontend_editing_view( - request, res.group(1), res.group(2), res.group(3)) - context = {} context.update(self.get_extra_context(request)) - context.update(kwargs.get('extra_context', {})) - kwargs['extra_context'] = context - return super(ItemEditor, self).change_view( - request, object_id, **kwargs) - - # The next two add support for sending a "saving done" signal as soon as - # all relevant data have been saved (especially all foreign key relations) - # This can be used to keep functionality dependend on item content happy. - # NOTE: These two can (and probably should) be replaced by overriding - # `save_related` as soon as we don't depend on Django<1.4 any more. - def response_add(self, request, obj, *args, **kwargs): - r = super(ItemEditor, self).response_add(request, obj, *args, **kwargs) - itemeditor_post_save_related.send( - sender=obj.__class__, instance=obj, created=True) - return r + context.update(kwargs.get("extra_context", {})) + kwargs["extra_context"] = context + return super().change_view(request, object_id, **kwargs) - def response_change(self, request, obj, *args, **kwargs): - r = super(ItemEditor, self).response_change( - request, obj, *args, **kwargs) + def save_related(self, request, form, formsets, change): + super().save_related(request, form, formsets, change) itemeditor_post_save_related.send( - sender=obj.__class__, instance=obj, created=False) - return r + sender=form.instance.__class__, instance=form.instance, created=not change + ) @property def change_form_template(self): - return self.get_template_list() - - def get_template_list(self): - # retained for backwards-compatibility, change_form_template wraps it opts = self.model._meta return [ - 'admin/feincms/%s/%s/item_editor.html' % ( - opts.app_label, opts.object_name.lower()), - 'admin/feincms/%s/item_editor.html' % opts.app_label, - 'admin/feincms/item_editor.html', + "admin/feincms/%s/%s/item_editor.html" + % (opts.app_label, opts.object_name.lower()), + "admin/feincms/%s/item_editor.html" % opts.app_label, + "admin/feincms/item_editor.html", ] def get_fieldsets(self, request, obj=None): @@ -330,10 +212,10 @@ def get_fieldsets(self, request, obj=None): Is it reasonable to assume this should always be included? """ - fieldsets = copy.deepcopy( - super(ItemEditor, self).get_fieldsets(request, obj)) + fieldsets = copy.deepcopy(super().get_fieldsets(request, obj)) + names = [f[0] for f in fieldsets] - if FEINCMS_CONTENT_FIELDSET_NAME not in dict(fieldsets).keys(): + if FEINCMS_CONTENT_FIELDSET_NAME not in names: fieldsets.append(FEINCMS_CONTENT_FIELDSET) return fieldsets @@ -344,8 +226,21 @@ def get_fieldsets(self, request, obj=None): recover_form_template = "admin/feincms/recover_form.html" - def render_revision_form(self, request, obj, version, context, - revert=False, recover=False): + # For Reversion < v2.0.0 + def render_revision_form( + self, request, obj, version, context, revert=False, recover=False + ): + context.update(self.get_extra_context(request)) + return super().render_revision_form( + request, obj, version, context, revert, recover + ) + + # For Reversion >= v2.0.0 + def _reversion_revisionform_view( + self, request, version, template_name, extra_context=None + ): + context = extra_context or {} context.update(self.get_extra_context(request)) - return super(ItemEditor, self).render_revision_form( - request, obj, version, context, revert, recover) + return super()._reversion_revisionform_view( + request, version, template_name, context + ) diff --git a/feincms/admin/thumbnail.py b/feincms/admin/thumbnail.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/feincms/admin/tree_editor.py b/feincms/admin/tree_editor.py index c4bb00a44..07aa1d0ab 100644 --- a/feincms/admin/tree_editor.py +++ b/feincms/admin/tree_editor.py @@ -1,25 +1,27 @@ # ------------------------------------------------------------------------ -# coding=utf-8 # ------------------------------------------------------------------------ -from __future__ import absolute_import, unicode_literals -from functools import reduce import json import logging +from functools import reduce -from django.contrib.admin.views import main from django.contrib.admin.actions import delete_selected -from django.contrib.staticfiles.templatetags.staticfiles import static +from django.contrib.admin.views import main +from django.contrib.auth import get_permission_codename from django.db.models import Q from django.http import ( - HttpResponse, HttpResponseBadRequest, - HttpResponseForbidden, HttpResponseNotFound, HttpResponseServerError) + HttpResponse, + HttpResponseBadRequest, + HttpResponseForbidden, + HttpResponseNotFound, + HttpResponseServerError, +) +from django.templatetags.static import static +from django.utils.encoding import force_str from django.utils.html import escape from django.utils.safestring import mark_safe -from django.utils.translation import ugettext_lazy as _, ugettext -from django.utils.encoding import force_text - +from django.utils.translation import gettext, gettext_lazy as _ from mptt.exceptions import InvalidMove from mptt.forms import MPTTAdminForm @@ -37,15 +39,14 @@ def django_boolean_icon(field_val, alt_text=None, title=None): """ # Origin: contrib/admin/templatetags/admin_list.py - BOOLEAN_MAPPING = {True: 'yes', False: 'no', None: 'unknown'} + BOOLEAN_MAPPING = {True: "yes", False: "no", None: "unknown"} alt_text = alt_text or BOOLEAN_MAPPING[field_val] if title is not None: title = 'title="%s" ' % title else: - title = '' - icon_url = static('feincms/img/icon-%s.gif' % BOOLEAN_MAPPING[field_val]) - return mark_safe( - '%s' % (icon_url, alt_text, title)) + title = "" + icon_url = static("feincms/img/icon-%s.gif" % BOOLEAN_MAPPING[field_val]) + return mark_safe(f'{alt_text}') def _build_tree_structure(queryset): @@ -63,34 +64,16 @@ def _build_tree_structure(queryset): all_nodes = {} mptt_opts = queryset.model._mptt_meta - items = queryset.order_by( - mptt_opts.tree_id_attr, - mptt_opts.left_attr) - values_list = items.values_list( - "pk", - "%s_id" % mptt_opts.parent_attr) - for p_id, parent_id in values_list: - all_nodes[p_id] = [] - - if parent_id: - if parent_id not in all_nodes: - # This happens very rarely, but protect against parents that - # we have yet to iteratove over. Happens with broken MPTT - # hierarchy. - all_nodes[parent_id] = [] - logger.warn( - "Incorrect MPTT hierarchy for %s, node %d has left_attr" - " < than one of its parents. Try rebuilding mptt data (use" - " '%s._default_manager.rebuild()').", - queryset.model.__name__, p_id, queryset.model.__name__) - - all_nodes[parent_id].append(p_id) - + items = queryset.order_by(mptt_opts.tree_id_attr, mptt_opts.left_attr).values_list( + "pk", "%s_id" % mptt_opts.parent_attr + ) + for p_id, parent_id in items: + all_nodes.setdefault(str(parent_id) if parent_id else 0, []).append(p_id) return all_nodes # ------------------------------------------------------------------------ -def ajax_editable_boolean_cell(item, attr, text='', override=None): +def ajax_editable_boolean_cell(item, attr, text="", override=None): """ Generate a html snippet for showing a boolean value on the admin page. Item is an object, attr is the attribute name we should display. Text @@ -105,7 +88,7 @@ def ajax_editable_boolean_cell(item, attr, text='', override=None): (useful for "disabled and you can't change it" situations). """ if text: - text = ' (%s)' % text + text = " (%s)" % text if override is not None: a = [django_boolean_icon(override, text), text] @@ -113,15 +96,13 @@ def ajax_editable_boolean_cell(item, attr, text='', override=None): value = getattr(item, attr) a = [ '' % ( - item.pk, - attr, - 'checked="checked"' if value else '', - )] + ' data-inplace-attribute="%s" %s>' + % (item.pk, attr, 'checked="checked"' if value else "") + ] a.insert(0, '
' % (attr, item.pk)) - a.append('
') - return ''.join(a) + a.append("") + return mark_safe("".join(a)) # ------------------------------------------------------------------------ @@ -137,9 +118,10 @@ class MyTreeEditor(TreeEditor): active_toggle = ajax_editable_boolean('active', _('is active')) """ + def _fn(self, item): return ajax_editable_boolean_cell(item, attr) - _fn.allow_tags = True + _fn.short_description = short_description _fn.editable_boolean_field = attr return _fn @@ -154,12 +136,15 @@ class ChangeList(main.ChangeList): def __init__(self, request, *args, **kwargs): self.user = request.user - super(ChangeList, self).__init__(request, *args, **kwargs) + super().__init__(request, *args, **kwargs) - def get_query_set(self, *args, **kwargs): + def get_queryset(self, *args, **kwargs): mptt_opts = self.model._mptt_meta - qs = super(ChangeList, self).get_query_set(*args, **kwargs).\ - order_by(mptt_opts.tree_id_attr, mptt_opts.left_attr) + qs = ( + super() + .get_queryset(*args, **kwargs) + .order_by(mptt_opts.tree_id_attr, mptt_opts.left_attr) + ) # Force has_filters, so that the expand/collapse in sidebar is visible self.has_filters = True return qs @@ -168,14 +153,15 @@ def get_results(self, request): mptt_opts = self.model._mptt_meta if settings.FEINCMS_TREE_EDITOR_INCLUDE_ANCESTORS: clauses = [ - Q(**{ - mptt_opts.tree_id_attr: tree_id, - mptt_opts.left_attr + '__lte': lft, - mptt_opts.right_attr + '__gte': rght, - }) for lft, rght, tree_id in self.query_set.values_list( - mptt_opts.left_attr, - mptt_opts.right_attr, - mptt_opts.tree_id_attr, + Q( + **{ + mptt_opts.tree_id_attr: tree_id, + mptt_opts.left_attr + "__lte": lft, + mptt_opts.right_attr + "__gte": rght, + } + ) + for lft, rght, tree_id in self.queryset.values_list( + mptt_opts.left_attr, mptt_opts.right_attr, mptt_opts.tree_id_attr ) ] # We could optimise a bit here by explicitely filtering out @@ -187,26 +173,25 @@ def get_results(self, request): # Note: Django ORM is smart enough to drop additional # clauses if the initial query set is unfiltered. This # is good. - queryset = self.query_set | self.model._default_manager.filter( - reduce(lambda p, q: p | q, clauses)) + self.queryset = self.queryset.union( + self.model._default_manager.filter( + reduce(lambda p, q: p | q, clauses) + ) + ).order_by(mptt_opts.tree_id_attr, mptt_opts.left_attr) - if hasattr(self, 'queryset'): - self.queryset = queryset - else: - # Django 1.5 and older - self.query_set = queryset - - super(ChangeList, self).get_results(request) + super().get_results(request) # Pre-process permissions because we still have the request here, # which is not passed in later stages in the tree editor for item in self.result_list: item.feincms_changeable = self.model_admin.has_change_permission( - request, item) + request, item + ) item.feincms_addable = ( item.feincms_changeable - and self.model_admin.has_add_permission(request, item)) + and self.model_admin.has_add_permission(request, item) + ) # ------------------------------------------------------------------------ @@ -227,33 +212,36 @@ class TreeEditor(ExtensionModelAdmin): list_per_page = 999999999 def __init__(self, *args, **kwargs): - super(TreeEditor, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) self.list_display = list(self.list_display) - if 'indented_short_title' not in self.list_display: - if self.list_display[0] == 'action_checkbox': - self.list_display[1] = 'indented_short_title' + if "indented_short_title" not in self.list_display: + if self.list_display[0] == "action_checkbox": + self.list_display[1] = "indented_short_title" else: - self.list_display[0] = 'indented_short_title' - self.list_display_links = ('indented_short_title',) + self.list_display[0] = "indented_short_title" + self.list_display_links = ("indented_short_title",) opts = self.model._meta self.change_list_template = [ - 'admin/feincms/%s/%s/tree_editor.html' % ( - opts.app_label, opts.object_name.lower()), - 'admin/feincms/%s/tree_editor.html' % opts.app_label, - 'admin/feincms/tree_editor.html', + "admin/feincms/%s/%s/tree_editor.html" + % (opts.app_label, opts.object_name.lower()), + "admin/feincms/%s/tree_editor.html" % opts.app_label, + "admin/feincms/tree_editor.html", ] - self.object_change_permission =\ - opts.app_label + '.' + opts.get_change_permission() - self.object_add_permission =\ - opts.app_label + '.' + opts.get_add_permission() - self.object_delete_permission =\ - opts.app_label + '.' + opts.get_delete_permission() + self.object_change_permission = ( + opts.app_label + "." + get_permission_codename("change", opts) + ) + self.object_add_permission = ( + opts.app_label + "." + get_permission_codename("add", opts) + ) + self.object_delete_permission = ( + opts.app_label + "." + get_permission_codename("delete", opts) + ) def changeable(self, item): - return getattr(item, 'feincms_changeable', True) + return getattr(item, "feincms_changeable", True) def indented_short_title(self, item): """ @@ -261,7 +249,7 @@ def indented_short_title(self, item): the object's depth in the hierarchy. """ mptt_opts = item._mptt_meta - r = '' + r = "" try: url = item.get_absolute_url() except (AttributeError,): @@ -270,28 +258,35 @@ def indented_short_title(self, item): if url: r = ( '') % (url, item.pk) + ' value="%s" id="_refkey_%d" />' + ) % (url, item.pk) - changeable_class = '' + changeable_class = "" if not self.changeable(item): - changeable_class = ' tree-item-not-editable' + changeable_class = " tree-item-not-editable" + tree_root_class = "" + if not item.parent_id: + tree_root_class = " tree-root" r += ( - '  ') % ( + '  ' + ) % ( item.pk, changeable_class, - 14 + getattr(item, mptt_opts.level_attr) * 18) + tree_root_class, + 14 + getattr(item, mptt_opts.level_attr) * 18, + ) -# r += '' - if hasattr(item, 'short_title') and callable(item.short_title): + # r += '' + if hasattr(item, "short_title") and callable(item.short_title): r += escape(item.short_title()) else: - r += escape('%s' % item) -# r += '' + r += escape("%s" % item) + # r += '' return mark_safe(r) - indented_short_title.short_description = _('title') - indented_short_title.allow_tags = True + + indented_short_title.short_description = _("title") def _collect_editable_booleans(self): """ @@ -299,7 +294,7 @@ def _collect_editable_booleans(self): want the user to be able to edit arbitrary fields by crafting an AJAX request by hand. """ - if hasattr(self, '_ajax_editable_booleans'): + if hasattr(self, "_ajax_editable_booleans"): return self._ajax_editable_booleans = {} @@ -312,44 +307,41 @@ def _collect_editable_booleans(self): except (AttributeError, TypeError): continue - attr = getattr(item, 'editable_boolean_field', None) + attr = getattr(item, "editable_boolean_field", None) if attr: - if hasattr(item, 'editable_boolean_result'): + if hasattr(item, "editable_boolean_result"): result_func = item.editable_boolean_result else: + def _fn(attr): return lambda self, instance: [ - ajax_editable_boolean_cell(instance, attr)] + ajax_editable_boolean_cell(instance, attr) + ] + result_func = _fn(attr) self._ajax_editable_booleans[attr] = result_func - def _refresh_changelist_caches(self): - """ - Refresh information used to show the changelist tree structure such as - inherited active/inactive states etc. - - XXX: This is somewhat hacky, but since it's an internal method, so be - it. - """ - - pass - def _toggle_boolean(self, request): """ Handle an AJAX toggle_boolean request """ try: - item_id = int(request.POST.get('item_id', None)) - attr = str(request.POST.get('attr', None)) - except: + item_id = int(request.POST.get("item_id", None)) + attr = str(request.POST.get("attr", None)) + except Exception: return HttpResponseBadRequest("Malformed request") if not request.user.is_staff: logger.warning( - "Denied AJAX request by non-staff \"%s\" to toggle boolean" - " %s for object #%s", request.user, attr, item_id) + 'Denied AJAX request by non-staff "%s" to toggle boolean' + " %s for object #%s", + request.user, + attr, + item_id, + ) return HttpResponseForbidden( - _("You do not have permission to modify this object")) + _("You do not have permission to modify this object") + ) self._collect_editable_booleans() @@ -363,15 +355,24 @@ def _toggle_boolean(self, request): if not self.has_change_permission(request, obj=obj): logger.warning( - "Denied AJAX request by \"%s\" to toggle boolean %s for" - " object %s", request.user, attr, item_id) + 'Denied AJAX request by "%s" to toggle boolean %s for object %s', + request.user, + attr, + item_id, + ) return HttpResponseForbidden( - _("You do not have permission to modify this object")) + _("You do not have permission to modify this object") + ) new_state = not getattr(obj, attr) logger.info( - "Toggle %s on #%d %s to %s by \"%s\"", - attr, obj.pk, obj, "on" if new_state else "off", request.user) + 'Toggle %s on #%d %s to %s by "%s"', + attr, + obj.pk, + obj, + "on" if new_state else "off", + request.user, + ) try: before_data = self._ajax_editable_booleans[attr](self, obj) @@ -379,24 +380,20 @@ def _toggle_boolean(self, request): setattr(obj, attr, new_state) obj.save() - # ???: Perhaps better a post_save signal? - self._refresh_changelist_caches() - # Construct html snippets to send back to client for status update data = self._ajax_editable_booleans[attr](self, obj) except Exception: - logger.exception( - "Unhandled exception while toggling %s on %s", attr, obj) - return HttpResponseServerError( - "Unable to toggle %s on %s" % (attr, obj)) + logger.exception("Unhandled exception while toggling %s on %s", attr, obj) + return HttpResponseServerError(f"Unable to toggle {attr} on {obj}") # Weed out unchanged cells to keep the updates small. This assumes # that the order a possible get_descendents() returns does not change # before and after toggling this attribute. Unlikely, but still... return HttpResponse( json.dumps([b for a, b in zip(before_data, data) if a != b]), - content_type="application/json") + content_type="application/json", + ) def get_changelist(self, request, **kwargs): return ChangeList @@ -407,28 +404,43 @@ def changelist_view(self, request, extra_context=None, *args, **kwargs): change list/actions page. """ - if 'actions_column' not in self.list_display: - self.list_display.append('actions_column') + if "actions_column" not in self.list_display: + self.list_display.append("actions_column") # handle common AJAX requests - if request.is_ajax(): - cmd = request.POST.get('__cmd') - if cmd == 'toggle_boolean': + if "__cmd" in request.POST: + cmd = request.POST.get("__cmd") + if cmd == "toggle_boolean": return self._toggle_boolean(request) - elif cmd == 'move_node': + elif cmd == "move_node": return self._move_node(request) - return HttpResponseBadRequest('Oops. AJAX request not understood.') - - self._refresh_changelist_caches() + return HttpResponseBadRequest("Oops. AJAX request not understood.") extra_context = extra_context or {} - queryset = self.queryset(request) - extra_context['tree_structure'] = mark_safe( - json.dumps(_build_tree_structure(queryset))) - return super(TreeEditor, self).changelist_view( - request, extra_context, *args, **kwargs) + extra_context.update( + { + "tree_structure": mark_safe( + json.dumps( + obj=_build_tree_structure(self.get_queryset(request)), + separators=(",", ":"), + ) + ), + "node_levels": mark_safe( + json.dumps( + dict( + self.get_queryset(request) + .order_by() + .values_list("pk", self.model._mptt_meta.level_attr) + ), + separators=(",", ":"), + ) + ), + } + ) + + return super().changelist_view(request, extra_context, *args, **kwargs) def has_add_permission(self, request, obj=None): """ @@ -441,7 +453,7 @@ def has_add_permission(self, request, obj=None): else: r = request.user.has_perm(perm) - return r and super(TreeEditor, self).has_add_permission(request) + return r and super().has_add_permission(request) def has_change_permission(self, request, obj=None): """ @@ -454,8 +466,7 @@ def has_change_permission(self, request, obj=None): else: r = request.user.has_perm(perm) - return r and super(TreeEditor, self).has_change_permission( - request, obj) + return r and super().has_change_permission(request, obj) def has_delete_permission(self, request, obj=None): """ @@ -468,30 +479,29 @@ def has_delete_permission(self, request, obj=None): else: r = request.user.has_perm(perm) - return r and super(TreeEditor, self).has_delete_permission( - request, obj) + return r and super().has_delete_permission(request, obj) def _move_node(self, request): - if hasattr(self.model.objects, 'move_node'): + if hasattr(self.model.objects, "move_node"): tree_manager = self.model.objects else: tree_manager = self.model._tree_manager - queryset = self.queryset(request) - cut_item = queryset.get(pk=request.POST.get('cut_item')) - pasted_on = queryset.get(pk=request.POST.get('pasted_on')) - position = request.POST.get('position') + queryset = self.get_queryset(request) + cut_item = queryset.get(pk=request.POST.get("cut_item")) + pasted_on = queryset.get(pk=request.POST.get("pasted_on")) + position = request.POST.get("position") if not self.has_change_permission(request, cut_item): - self.message_user(request, _('No permission')) - return HttpResponse('FAIL') + self.message_user(request, _("No permission")) + return HttpResponse("FAIL") - if position in ('last-child', 'left', 'right'): + if position in ("last-child", "left", "right"): try: tree_manager.move_node(cut_item, pasted_on, position) except InvalidMove as e: - self.message_user(request, '%s' % e) - return HttpResponse('FAIL') + self.message_user(request, "%s" % e) + return HttpResponse("FAIL") # Ensure that model save methods have been run (required to # update Page._cached_url values, might also be helpful for other @@ -500,12 +510,12 @@ def _move_node(self, request): item.save() self.message_user( - request, - ugettext('%s has been moved to a new position.') % cut_item) - return HttpResponse('OK') + request, gettext("%s has been moved to a new position.") % cut_item + ) + return HttpResponse("OK") - self.message_user(request, _('Did not understand moving instruction.')) - return HttpResponse('FAIL') + self.message_user(request, _("Did not understand moving instruction.")) + return HttpResponse("FAIL") def _actions_column(self, instance): if self.changeable(instance): @@ -513,9 +523,9 @@ def _actions_column(self, instance): return [] def actions_column(self, instance): - return ' '.join(self._actions_column(instance)) - actions_column.allow_tags = True - actions_column.short_description = _('actions') + return mark_safe(" ".join(self._actions_column(instance))) + + actions_column.short_description = _("actions") def delete_selected_tree(self, modeladmin, request, queryset): """ @@ -524,21 +534,29 @@ def delete_selected_tree(self, modeladmin, request, queryset): trigger the post_delete hooks.) """ # If this is True, the confirmation page has been displayed - if request.POST.get('post'): + if request.POST.get("post"): n = 0 - for obj in queryset: - if self.has_delete_permission(request, obj): - obj.delete() - n += 1 - obj_display = force_text(obj) - self.log_deletion(request, obj, obj_display) - else: - logger.warning( - "Denied delete request by \"%s\" for object #%s", - request.user, obj.id) + # TODO: The disable_mptt_updates / rebuild is a work around + # for what seems to be a mptt problem when deleting items + # in a loop. Revisit this, there should be a better solution. + with queryset.model.objects.disable_mptt_updates(): + for obj in queryset: + if self.has_delete_permission(request, obj): + obj.delete() + n += 1 + obj_display = force_str(obj) + self.log_deletion(request, obj, obj_display) + else: + logger.warning( + 'Denied delete request by "%s" for object #%s', + request.user, + obj.id, + ) + if n > 0: + queryset.model.objects.rebuild() self.message_user( - request, - _("Successfully deleted %(count)d items.") % {"count": n}) + request, _("Successfully deleted %(count)d items.") % {"count": n} + ) # Return None to display the change list page again return None else: @@ -546,10 +564,11 @@ def delete_selected_tree(self, modeladmin, request, queryset): return delete_selected(self, request, queryset) def get_actions(self, request): - actions = super(TreeEditor, self).get_actions(request) - if 'delete_selected' in actions: - actions['delete_selected'] = ( + actions = super().get_actions(request) + if "delete_selected" in actions: + actions["delete_selected"] = ( self.delete_selected_tree, - 'delete_selected', - _("Delete selected %(verbose_name_plural)s")) + "delete_selected", + _("Delete selected %(verbose_name_plural)s"), + ) return actions diff --git a/feincms/apps.py b/feincms/apps.py new file mode 100644 index 000000000..8e5394646 --- /dev/null +++ b/feincms/apps.py @@ -0,0 +1,17 @@ +def __getattr__(key): + # Work around Django 3.2's autoloading of *.apps modules (AppConfig + # autodiscovery) + if key in { + "ApplicationContent", + "app_reverse", + "app_reverse_lazy", + "permalink", + "UnpackTemplateResponse", + "standalone", + "unpack", + }: + from feincms.content.application import models + + return getattr(models, key) + + raise AttributeError("Unknown attribute '%s'" % key) diff --git a/feincms/content/application/models.py b/feincms/content/application/models.py index bdc328ef5..46d5b44c9 100644 --- a/feincms/content/application/models.py +++ b/feincms/content/application/models.py @@ -1,25 +1,26 @@ -""" -Third-party application inclusion support. -""" - -from __future__ import absolute_import, unicode_literals - +import warnings +from collections import OrderedDict from email.utils import parsedate +from functools import partial, wraps from time import mktime -from random import SystemRandom -import re from django.conf import settings from django.core.cache import cache -from django.core.urlresolvers import ( - Resolver404, resolve, reverse, NoReverseMatch) from django.db import models -from django.db.models import signals from django.http import HttpResponse -from django.utils.functional import curry as partial, lazy, wraps +from django.template.response import TemplateResponse +from django.urls import ( + NoReverseMatch, + Resolver404, + get_script_prefix, + resolve, + reverse, + set_script_prefix, +) +from django.utils.functional import lazy from django.utils.http import http_date from django.utils.safestring import mark_safe -from django.utils.translation import get_language, ugettext_lazy as _ +from django.utils.translation import get_language, gettext_lazy as _ from feincms.admin.item_editor import ItemEditorForm from feincms.contrib.fields import JSONField @@ -27,20 +28,70 @@ from feincms.utils import get_object -def cycle_app_reverse_cache(*args, **kwargs): - """Does not really empty the cache; instead it adds a random element to the - cache key generation which guarantees that the cache does not yet contain - values for all newly generated keys""" - cache.set('app_reverse_cache_generation', str(SystemRandom().random())) +APP_REVERSE_CACHE_TIMEOUT = 3 + + +__all__ = ( + "ApplicationContent", + "app_reverse", + "app_reverse_lazy", + "permalink", + "UnpackTemplateResponse", + "standalone", + "unpack", +) + + +class UnpackTemplateResponse(TemplateResponse): + """ + Completely the same as marking applicationcontent-contained views with + the ``feincms.views.decorators.unpack`` decorator. + """ + + _feincms_unpack = True + + +def standalone(view_func): + """ + Marks the view method as standalone view; this means that + ``HttpResponse`` objects returned from ``ApplicationContent`` + are returned directly, without further processing. + """ + + def inner(request, *args, **kwargs): + response = view_func(request, *args, **kwargs) + if isinstance(response, HttpResponse): + response.standalone = True + return response + + return wraps(view_func)(inner) + + +def unpack(view_func): + """ + Marks the returned response as to-be-unpacked if it is a + ``TemplateResponse``. + """ + + def inner(request, *args, **kwargs): + response = view_func(request, *args, **kwargs) + if isinstance(response, TemplateResponse): + response._feincms_unpack = True + return response + return wraps(view_func)(inner) -# Set the app_reverse_cache_generation value once per startup (at least). -# This protects us against offline modifications of the database. -cycle_app_reverse_cache() + +def cycle_app_reverse_cache(*args, **kwargs): + warnings.warn( + "cycle_app_reverse_cache does nothing and will be removed in" + " a future version of FeinCMS.", + DeprecationWarning, + stacklevel=2, + ) -def app_reverse(viewname, urlconf=None, args=None, kwargs=None, prefix=None, - *vargs, **vkwargs): +def app_reverse(viewname, urlconf=None, args=None, kwargs=None, *vargs, **vkwargs): """ Reverse URLs from application contents @@ -61,50 +112,40 @@ def app_reverse(viewname, urlconf=None, args=None, kwargs=None, prefix=None, # First parameter might be a request instead of an urlconf path, so # we'll try to be helpful and extract the current urlconf from it - extra_context = getattr(urlconf, '_feincms_extra_context', {}) - appconfig = extra_context.get('app_config', {}) - urlconf = appconfig.get('urlconf_path', urlconf) - - cache_generation = cache.get('app_reverse_cache_generation') - if cache_generation is None: - # This might never happen. Still, better be safe than sorry. - cycle_app_reverse_cache() - cache_generation = cache.get('app_reverse_cache_generation') - - cache_key = '%s-%s-%s-%s' % ( - urlconf, - get_language(), - getattr(settings, 'SITE_ID', 0), - cache_generation) + extra_context = getattr(urlconf, "_feincms_extra_context", {}) + appconfig = extra_context.get("app_config", {}) + urlconf = appconfig.get("urlconf_path", urlconf) + appcontent_class = ApplicationContent._feincms_content_models[0] + cache_key = appcontent_class.app_reverse_cache_key(urlconf) url_prefix = cache.get(cache_key) if url_prefix is None: - appcontent_class = ApplicationContent._feincms_content_models[0] content = appcontent_class.closest_match(urlconf) if content is not None: if urlconf in appcontent_class.ALL_APPS_CONFIG: # We have an overridden URLconf app_config = appcontent_class.ALL_APPS_CONFIG[urlconf] - urlconf = app_config['config'].get('urls', urlconf) + urlconf = app_config["config"].get("urls", urlconf) prefix = content.parent.get_absolute_url() - prefix += '/' if prefix[-1] != '/' else '' + prefix += "/" if prefix[-1] != "/" else "" url_prefix = (urlconf, prefix) - cache.set(cache_key, url_prefix) + cache.set(cache_key, url_prefix, timeout=APP_REVERSE_CACHE_TIMEOUT) if url_prefix: # vargs and vkwargs are used to send through additional parameters # which are uninteresting to us (such as current_app) - return reverse( - viewname, - url_prefix[0], - args=args, - kwargs=kwargs, - prefix=url_prefix[1], - *vargs, **vkwargs) + prefix = get_script_prefix() + try: + set_script_prefix(url_prefix[1]) + return reverse( + viewname, url_prefix[0], args=args, kwargs=kwargs, *vargs, **vkwargs + ) + finally: + set_script_prefix(prefix) raise NoReverseMatch("Unable to find ApplicationContent for %r" % urlconf) @@ -126,12 +167,11 @@ class MyModel(models.Model): def get_absolute_url(self): return ('myapp.urls', 'model_detail', (), {'slug': self.slug}) """ + def inner(*args, **kwargs): return app_reverse(*func(*args, **kwargs)) - return wraps(func)(inner) - -APPLICATIONCONTENT_RE = re.compile(r'^([^/]+)/([^/]+)$') + return wraps(func)(inner) class ApplicationContent(models.Model): @@ -140,12 +180,12 @@ class ApplicationContent(models.Model): # MyBlogApp for blog ") parameters = JSONField(null=True, editable=False) - ALL_APPS_CONFIG = {} + ALL_APPS_CONFIG = OrderedDict() class Meta: abstract = True - verbose_name = _('application content') - verbose_name_plural = _('application contents') + verbose_name = _("application content") + verbose_name_plural = _("application contents") @classmethod def initialize_type(cls, APPLICATIONS): @@ -154,7 +194,8 @@ def initialize_type(cls, APPLICATIONS): raise ValueError( "APPLICATIONS must be provided with tuples containing at" " least two parameters (urls, name) and an optional extra" - " config dict") + " config dict" + ) urls, name = i[0:2] @@ -164,20 +205,20 @@ def initialize_type(cls, APPLICATIONS): if not isinstance(app_conf, dict): raise ValueError( "The third parameter of an APPLICATIONS entry must be" - " a dict or the name of one!") + " a dict or the name of one!" + ) else: app_conf = {} - cls.ALL_APPS_CONFIG[urls] = { - "urls": urls, - "name": name, - "config": app_conf - } + cls.ALL_APPS_CONFIG[urls] = {"urls": urls, "name": name, "config": app_conf} cls.add_to_class( - 'urlconf_path', - models.CharField(_('application'), max_length=100, choices=[ - (c['urls'], c['name']) for c in cls.ALL_APPS_CONFIG.values()]) + "urlconf_path", + models.CharField( + _("application"), + max_length=100, + choices=[(c["urls"], c["name"]) for c in cls.ALL_APPS_CONFIG.values()], + ), ) class ApplicationContentItemEditorForm(ItemEditorForm): @@ -185,8 +226,7 @@ class ApplicationContentItemEditorForm(ItemEditorForm): custom_fields = {} def __init__(self, *args, **kwargs): - super(ApplicationContentItemEditorForm, self).__init__( - *args, **kwargs) + super().__init__(*args, **kwargs) instance = kwargs.get("instance", None) @@ -195,24 +235,25 @@ def __init__(self, *args, **kwargs): # TODO use urlconf_path from POST if set # urlconf_path = request.POST.get('...urlconf_path', # instance.urlconf_path) - self.app_config = cls.ALL_APPS_CONFIG[ - instance.urlconf_path]['config'] + self.app_config = cls.ALL_APPS_CONFIG[instance.urlconf_path][ + "config" + ] except KeyError: self.app_config = {} self.custom_fields = {} - admin_fields = self.app_config.get('admin_fields', {}) + admin_fields = self.app_config.get("admin_fields", {}) if isinstance(admin_fields, dict): self.custom_fields.update(admin_fields) else: get_fields = get_object(admin_fields) - self.custom_fields.update( - get_fields(self, *args, **kwargs)) + self.custom_fields.update(get_fields(self, *args, **kwargs)) + params = self.instance.parameters for k, v in self.custom_fields.items(): + v.initial = params.get(k) self.fields[k] = v - params = self.instance.parameters if k in params: self.fields[k].initial = params[k] @@ -222,12 +263,13 @@ def save(self, commit=True, *args, **kwargs): # get the model so we can set .parameters to the values of our # custom fields before calling save(commit=True) - m = super(ApplicationContentItemEditorForm, self).save( - commit=False, *args, **kwargs) + m = super().save(commit=False, *args, **kwargs) - m.parameters = dict( - (k, self.cleaned_data[k]) - for k in self.custom_fields if k in self.cleaned_data) + m.parameters = { + k: self.cleaned_data[k] + for k in self.custom_fields + if k in self.cleaned_data + } if commit: m.save(**kwargs) @@ -238,18 +280,11 @@ def save(self, commit=True, *args, **kwargs): # embedded instances: cls.feincms_item_editor_form = ApplicationContentItemEditorForm - # Clobber the app_reverse cache when saving application contents - # and/or pages - page_class = cls.parent.field.rel.to - signals.post_save.connect(cycle_app_reverse_cache, sender=cls) - signals.post_delete.connect(cycle_app_reverse_cache, sender=cls) - signals.post_save.connect(cycle_app_reverse_cache, sender=page_class) - signals.post_delete.connect(cycle_app_reverse_cache, sender=page_class) - def __init__(self, *args, **kwargs): - super(ApplicationContent, self).__init__(*args, **kwargs) - self.app_config = self.ALL_APPS_CONFIG.get( - self.urlconf_path, {}).get('config', {}) + super().__init__(*args, **kwargs) + self.app_config = self.ALL_APPS_CONFIG.get(self.urlconf_path, {}).get( + "config", {} + ) def process(self, request, **kw): page_url = self.parent.get_absolute_url() @@ -259,21 +294,20 @@ def process(self, request, **kw): if "path_mapper" in self.app_config: path_mapper = get_object(self.app_config["path_mapper"]) path, page_url = path_mapper( - request.path, - page_url, - appcontent_parameters=self.parameters + request.path, page_url, appcontent_parameters=self.parameters ) else: - path = request._feincms_extra_context['extra_path'] + path = request._feincms_extra_context["extra_path"] # Resolve the module holding the application urls. - urlconf_path = self.app_config.get('urls', self.urlconf_path) + urlconf_path = self.app_config.get("urls", self.urlconf_path) try: fn, args, kwargs = resolve(path, urlconf_path) except (ValueError, Resolver404): - raise Resolver404(str('Not found (resolving %r in %r failed)') % ( - path, urlconf_path)) + raise Resolver404( + f"Not found (resolving {path!r} in {urlconf_path!r} failed)" + ) # Variables from the ApplicationContent parameters are added to request # so we can expose them to our templates via the appcontent_parameters @@ -281,19 +315,14 @@ def process(self, request, **kw): request._feincms_extra_context.update(self.parameters) # Save the application configuration for reuse elsewhere - request._feincms_extra_context.update({ - 'app_config': dict( - self.app_config, - urlconf_path=self.urlconf_path, - ), - }) + request._feincms_extra_context.update( + {"app_config": dict(self.app_config, urlconf_path=self.urlconf_path)} + ) view_wrapper = self.app_config.get("view_wrapper", None) if view_wrapper: fn = partial( - get_object(view_wrapper), - view=fn, - appcontent_parameters=self.parameters + get_object(view_wrapper), view=fn, appcontent_parameters=self.parameters ) output = fn(request, *args, **kwargs) @@ -302,47 +331,68 @@ def process(self, request, **kw): if self.send_directly(request, output): return output elif output.status_code == 200: + if self.unpack(request, output) and "view" in kw: + # Handling of @unpack and UnpackTemplateResponse + kw["view"].template_name = output.template_name + kw["view"].request._feincms_extra_context.update( + output.context_data + ) + + else: + # If the response supports deferred rendering, render the + # response right now. We do not handle template response + # middleware. + if hasattr(output, "render") and callable(output.render): + output.render() + + self.rendered_result = mark_safe(output.content.decode("utf-8")) - # If the response supports deferred rendering, render the - # response right now. We do not handle template response - # middleware. - if hasattr(output, 'render') and callable(output.render): - output.render() - - self.rendered_result = mark_safe( - output.content.decode('utf-8')) self.rendered_headers = {} # Copy relevant headers for later perusal - for h in ('Cache-Control', 'Last-Modified', 'Expires'): + for h in ("Cache-Control", "Last-Modified", "Expires"): if h in output: - self.rendered_headers.setdefault( - h, []).append(output[h]) + self.rendered_headers.setdefault(h, []).append(output[h]) + + application_cookies = output.cookies + if application_cookies: + self.rendered_headers["X-Feincms-Cookie"] = application_cookies + + elif isinstance(output, tuple) and "view" in kw: + kw["view"].template_name = output[0] + kw["view"].request._feincms_extra_context.update(output[1]) - elif isinstance(output, tuple) and 'view' in kw: - kw['view'].template_name = output[0] - kw['view'].request._feincms_extra_context.update(output[1]) else: self.rendered_result = mark_safe(output) return True # successful def send_directly(self, request, response): - mimetype = response.get('Content-Type', 'text/plain') - if ';' in mimetype: - mimetype = mimetype.split(';')[0] + mimetype = response.get("Content-Type", "text/plain") + if ";" in mimetype: + mimetype = mimetype.split(";")[0] mimetype = mimetype.strip() - return (response.status_code != 200 - or request.is_ajax() - or getattr(response, 'standalone', False) - or mimetype not in ('text/html', 'text/plain')) + is_ajax = ( + request.is_ajax() + if hasattr(request, "is_ajax") + else request.headers.get("x-requested-with") == "XMLHttpRequest" + ) + return ( + response.status_code != 200 + or is_ajax + or getattr(response, "standalone", False) + or mimetype not in ("text/html", "text/plain") + ) + + def unpack(self, request, response): + return getattr(response, "_feincms_unpack", False) def render(self, **kwargs): - return getattr(self, 'rendered_result', '') + return getattr(self, "rendered_result", "") def finalize(self, request, response): - headers = getattr(self, 'rendered_headers', None) + headers = getattr(self, "rendered_headers", None) if headers: self._update_response_headers(request, response, headers) @@ -355,40 +405,71 @@ def _update_response_headers(self, request, response, headers): # Ideally, for the Cache-Control header, we'd want to do some # intelligent combining, but that's hard. Let's just collect and unique # them and let the client worry about that. - cc_headers = set(('must-revalidate',)) - for x in (cc.split(",") for cc in headers.get('Cache-Control', ())): - cc_headers |= set((s.strip() for s in x)) + cc_headers = {"must-revalidate"} + for x in (cc.split(",") for cc in headers.get("Cache-Control", ())): + cc_headers |= {s.strip() for s in x} if len(cc_headers): - response['Cache-Control'] = ", ".join(cc_headers) - else: # Default value - response['Cache-Control'] = 'no-cache, must-revalidate' + response["Cache-Control"] = ", ".join(cc_headers) + else: # Default value + response["Cache-Control"] = "no-cache, must-revalidate" # Check all Last-Modified headers, choose the latest one - lm_list = [parsedate(x) for x in headers.get('Last-Modified', ())] + lm_list = [parsedate(x) for x in headers.get("Last-Modified", ())] if len(lm_list) > 0: - response['Last-Modified'] = http_date(mktime(max(lm_list))) + response["Last-Modified"] = http_date(mktime(max(lm_list))) # Check all Expires headers, choose the earliest one - lm_list = [parsedate(x) for x in headers.get('Expires', ())] + lm_list = [parsedate(x) for x in headers.get("Expires", ())] if len(lm_list) > 0: - response['Expires'] = http_date(mktime(min(lm_list))) + response["Expires"] = http_date(mktime(min(lm_list))) + + # Add all cookies + cookies = headers.get("X-Feincms-Cookie", None) + if cookies: + for kookie, val in cookies.items(): + response.set_cookie( + kookie, + value=val.value, + max_age=val["max-age"], + expires=val["expires"], + path=val["path"], + domain=val["domain"], + secure=val["secure"], + httponly=val["httponly"], + samesite=val["samesite"], + ) + + @classmethod + def app_reverse_cache_key(cls, urlconf_path, **kwargs): + return "FEINCMS:{}:APPCONTENT:{}:{}".format( + getattr(settings, "SITE_ID", 0), + get_language(), + urlconf_path, + ) @classmethod def closest_match(cls, urlconf_path): - page_class = cls.parent.field.rel.to + try: + page_class = cls.parent.field.remote_field.model + except AttributeError: + page_class = cls.parent.field.rel.to - contents = cls.objects.filter( - parent__in=page_class.objects.active(), - urlconf_path=urlconf_path, - ).order_by('pk').select_related('parent') + contents = ( + cls.objects.filter( + parent__in=page_class.objects.active(), urlconf_path=urlconf_path + ) + .order_by("pk") + .select_related("parent") + ) if len(contents) > 1: try: current = short_language_code(get_language()) return [ - content for content in contents if - short_language_code(content.parent.language) == current + content + for content in contents + if short_language_code(content.parent.language) == current ][0] except (AttributeError, IndexError): diff --git a/feincms/content/comments/models.py b/feincms/content/comments/models.py deleted file mode 100644 index 3ad59260c..000000000 --- a/feincms/content/comments/models.py +++ /dev/null @@ -1,112 +0,0 @@ -# ------------------------------------------------------------------------ -# coding=utf-8 -# ------------------------------------------------------------------------ -# -# Created by Martin J. Laubach on 08.01.10. -# skyl wuz here (11.05.10) -# -# ------------------------------------------------------------------------ - -""" -Embed a comment list and comment form anywhere. Uses the standard -``django.contrib.comments`` application. -""" - -from __future__ import absolute_import, unicode_literals - -from django.contrib import comments -from django.contrib.comments.views.comments import post_comment -from django.db import models -from django.http import HttpResponseRedirect -from django.template import RequestContext -from django.template.loader import render_to_string -from django.utils.translation import ugettext_lazy as _ - -from feincms.admin.item_editor import ItemEditorForm - - -# ------------------------------------------------------------------------ -class CommentsContent(models.Model): - comments_enabled = models.BooleanField( - _('enabled'), default=True, - help_text=_('New comments may be added')) - - class Meta: - abstract = True - verbose_name = _('comments') - verbose_name_plural = _('comments') - - @classmethod - def initialize_type(cls): - class CommentContentAdminForm(ItemEditorForm): - def __init__(self, *args, **kwargs): - super(CommentContentAdminForm, self).__init__(*args, **kwargs) - parent = kwargs.get('instance', None) - if parent is not None: - f = self.fields['comments_enabled'] - r = f.help_text - r += '
' - comments_model = comments.get_model() - for c in comments_model.objects.for_model( - parent.parent).order_by('-submit_date'): - r += ( - '
' - '# %(pk)d %(comment)s - %(is_public)s
') % { - 'pk': c.id, - 'comment': c.comment[:80], - 'is_public': ( - _('public') if c.is_public - else _('not public')), - 'app': comments_model._meta.app_label, - 'model': comments_model._meta.module_name, - } - f.help_text = r - - cls.feincms_item_editor_form = CommentContentAdminForm - - def process(self, request, **kwargs): - parent_type = self.parent.__class__.__name__.lower() - - comment_page = self.parent - if (hasattr(comment_page, 'original_translation') - and comment_page.original_translation): - comment_page = comment_page.original_translation - - f = None - if self.comments_enabled and request.POST: - - # I guess the drawback is that this page can't handle any other - # types of posts just the comments for right now, but if we just - # post to the current path and handle it this way .. at least it - # works for now. - - # extra = request._feincms_extra_context.get('page_extra_path', ()) - # if len(extra) > 0 and extra[0] == "post-comment": - - r = post_comment(request, next=comment_page.get_absolute_url()) - - if isinstance(r, HttpResponseRedirect): - return r - - f = comments.get_form()(comment_page, data=request.POST) - - if f is None: - f = comments.get_form()(comment_page) - - self.rendered_output = render_to_string( - [ - 'content/comments/%s.html' % parent_type, - 'content/comments/default-site.html', - 'content/comments/default.html', - ], - RequestContext(request, { - 'content': self, - 'feincms_page': self.parent, - 'parent': comment_page, - 'form': f, - }), - ) - - def render(self, **kwargs): - return getattr(self, 'rendered_output', '') diff --git a/feincms/content/contactform/__init__.py b/feincms/content/contactform/__init__.py index e69de29bb..2b97afb5a 100644 --- a/feincms/content/contactform/__init__.py +++ b/feincms/content/contactform/__init__.py @@ -0,0 +1,10 @@ +# flake8: noqa + +import warnings + + +warnings.warn( + "The contactform content has been deprecated. Use form-designer instead.", + DeprecationWarning, + stacklevel=2, +) diff --git a/feincms/content/contactform/models.py b/feincms/content/contactform/models.py index 35cb2f6b1..6699703cc 100644 --- a/feincms/content/contactform/models.py +++ b/feincms/content/contactform/models.py @@ -5,25 +5,20 @@ ``form=YourClass`` argument to the ``create_content_type`` call. """ -from __future__ import absolute_import, unicode_literals - from django import forms from django.core.mail import send_mail from django.db import models from django.http import HttpResponseRedirect -from django.template import RequestContext from django.template.loader import render_to_string -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import gettext_lazy as _ class ContactForm(forms.Form): - name = forms.CharField(label=_('name')) - email = forms.EmailField(label=_('email')) - subject = forms.CharField(label=_('subject')) + name = forms.CharField(label=_("name")) + email = forms.EmailField(label=_("email")) + subject = forms.CharField(label=_("subject")) - content = forms.CharField( - widget=forms.Textarea, required=False, - label=_('content')) + content = forms.CharField(widget=forms.Textarea, required=False, label=_("content")) class ContactFormContent(models.Model): @@ -34,8 +29,8 @@ class ContactFormContent(models.Model): class Meta: abstract = True - verbose_name = _('contact form') - verbose_name_plural = _('contact forms') + verbose_name = _("contact form") + verbose_name_plural = _("contact forms") @classmethod def initialize_type(cls, form=None): @@ -43,41 +38,40 @@ def initialize_type(cls, form=None): cls.form = form def process(self, request, **kwargs): - if request.GET.get('_cf_thanks'): + if request.GET.get("_cf_thanks"): self.rendered_output = render_to_string( - 'content/contactform/thanks.html', - context_instance=RequestContext(request)) + "content/contactform/thanks.html", {"content": self}, request=request + ) return - if request.method == 'POST': + if request.method == "POST": form = self.form(request.POST) if form.is_valid(): send_mail( - form.cleaned_data['subject'], - render_to_string('content/contactform/email.txt', { - 'data': form.cleaned_data, - }), - form.cleaned_data['email'], + form.cleaned_data["subject"], + render_to_string( + "content/contactform/email.txt", {"data": form.cleaned_data} + ), + form.cleaned_data["email"], [self.email], fail_silently=True, ) - return HttpResponseRedirect('?_cf_thanks=1') + return HttpResponseRedirect("?_cf_thanks=1") else: - initial = {'subject': self.subject} - if request.user.is_authenticated(): - initial['email'] = request.user.email - initial['name'] = request.user.get_full_name() + initial = {"subject": self.subject} + if request.user.is_authenticated: + initial["email"] = request.user.email + initial["name"] = request.user.get_full_name() form = self.form(initial=initial) self.rendered_output = render_to_string( - 'content/contactform/form.html', { - 'content': self, - 'form': form, - }, - context_instance=RequestContext(request)) + "content/contactform/form.html", + {"content": self, "form": form}, + request=request, + ) def render(self, **kwargs): - return getattr(self, 'rendered_output', '') + return getattr(self, "rendered_output", "") diff --git a/feincms/content/file/models.py b/feincms/content/file/models.py index 5b86d9f95..7fc8bec58 100644 --- a/feincms/content/file/models.py +++ b/feincms/content/file/models.py @@ -3,15 +3,13 @@ instead. """ -from __future__ import absolute_import, unicode_literals - import os from django.db import models -from django.template.loader import render_to_string -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import gettext_lazy as _ from feincms import settings +from feincms.utils.tuple import AutoRenderTuple class FileContent(models.Model): @@ -20,20 +18,20 @@ class FileContent(models.Model): title = models.CharField(max_length=200) file = models.FileField( - _('file'), max_length=255, - upload_to=os.path.join(settings.FEINCMS_UPLOAD_PREFIX, 'filecontent')) + _("file"), + max_length=255, + upload_to=os.path.join(settings.FEINCMS_UPLOAD_PREFIX, "filecontent"), + ) class Meta: abstract = True - verbose_name = _('file') - verbose_name_plural = _('files') + verbose_name = _("file") + verbose_name_plural = _("files") def render(self, **kwargs): - return render_to_string( - [ - 'content/file/%s.html' % self.region, - 'content/file/default.html', - ], - {'content': self}, - context_instance=kwargs.get('context'), + return AutoRenderTuple( + ( + ["content/file/%s.html" % self.region, "content/file/default.html"], + {"content": self}, + ) ) diff --git a/example/__init__.py b/feincms/content/filer/__init__.py similarity index 100% rename from example/__init__.py rename to feincms/content/filer/__init__.py diff --git a/feincms/content/filer/models.py b/feincms/content/filer/models.py new file mode 100644 index 000000000..df48d7147 --- /dev/null +++ b/feincms/content/filer/models.py @@ -0,0 +1,117 @@ +from django.contrib import admin +from django.core.exceptions import ImproperlyConfigured +from django.db import models +from django.utils.translation import gettext_lazy as _ + +from feincms.admin.item_editor import FeinCMSInline +from feincms.utils.tuple import AutoRenderTuple + + +try: + from filer.fields.file import FilerFileField + from filer.fields.image import FilerImageField +except ImportError: + __all__ = () + +else: + __all__ = ( + "MediaFileContentInline", + "ContentWithFilerFile", + "FilerFileContent", + "FilerImageContent", + ) + + class MediaFileContentInline(FeinCMSInline): + radio_fields = {"type": admin.VERTICAL} + + class ContentWithFilerFile(models.Model): + """ + File content + """ + + feincms_item_editor_inline = MediaFileContentInline + + class Meta: + abstract = True + + def render(self, **kwargs): + return AutoRenderTuple( + ( + [ + f"content/filer/{self.file_type}_{self.type}.html", + "content/filer/%s.html" % self.type, + "content/filer/%s.html" % self.file_type, + "content/filer/default.html", + ], + {"content": self}, + ) + ) + + class FilerFileContent(ContentWithFilerFile): + mediafile = FilerFileField( + verbose_name=_("file"), related_name="+", on_delete=models.CASCADE + ) + file_type = "file" + type = "download" + + class Meta: + abstract = True + verbose_name = _("file") + verbose_name_plural = _("files") + + class FilerImageContent(ContentWithFilerFile): + """ + Create a media file content as follows:: + + from feincms.contents import FilerImageContent + Page.create_content_type(FilerImageContent, TYPE_CHOICES=( + ('inline', _('Default')), + ('lightbox', _('Lightbox')), + ('whatever', _('Whatever')), + )) + + For a media file of type 'image' and type 'lightbox', the following + templates are tried in order: + + * content/mediafile/image_lightbox.html + * content/mediafile/lightbox.html + * content/mediafile/image.html + * content/mediafile/default.html + + The context contains ``content`` and ``request`` (if available). + + The content.mediafile attribute are as follows (selection): + label, description, default_caption, default_alt_text, + author, must_always_publish_author_credit, + must_always_publish_copyright, date_taken, file, id, is_public, url + """ + + mediafile = FilerImageField( + verbose_name=_("image"), related_name="+", on_delete=models.CASCADE + ) + caption = models.CharField(_("caption"), max_length=1000, blank=True) + url = models.CharField(_("URL"), max_length=1000, blank=True) + + file_type = "image" + + class Meta: + abstract = True + verbose_name = _("image") + verbose_name_plural = _("images") + + @classmethod + def initialize_type(cls, TYPE_CHOICES=None): + if TYPE_CHOICES is None: + raise ImproperlyConfigured( + "You have to set TYPE_CHOICES when creating a %s" % cls.__name__ + ) + + cls.add_to_class( + "type", + models.CharField( + _("type"), + max_length=20, + choices=TYPE_CHOICES, + default=TYPE_CHOICES[0][0], + ), + ) diff --git a/feincms/content/image/models.py b/feincms/content/image/models.py index 69ff2da2d..1593ba5ec 100644 --- a/feincms/content/image/models.py +++ b/feincms/content/image/models.py @@ -3,16 +3,14 @@ instead. """ -from __future__ import absolute_import, unicode_literals - import os from django.db import models -from django.template.loader import render_to_string -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import gettext_lazy as _ from feincms import settings from feincms.templatetags import feincms_thumbnail +from feincms.utils.tuple import AutoRenderTuple class ImageContent(models.Model): @@ -43,52 +41,54 @@ class ImageContent(models.Model): """ image = models.ImageField( - _('image'), max_length=255, - upload_to=os.path.join(settings.FEINCMS_UPLOAD_PREFIX, 'imagecontent')) + _("image"), + max_length=255, + upload_to=os.path.join(settings.FEINCMS_UPLOAD_PREFIX, "imagecontent"), + ) alt_text = models.CharField( - _('alternate text'), max_length=255, blank=True, - help_text=_('Description of image')) - caption = models.CharField(_('caption'), max_length=255, blank=True) + _("alternate text"), + max_length=255, + blank=True, + help_text=_("Description of image"), + ) + caption = models.CharField(_("caption"), max_length=255, blank=True) class Meta: abstract = True - verbose_name = _('image') - verbose_name_plural = _('images') + verbose_name = _("image") + verbose_name_plural = _("images") def render(self, **kwargs): - templates = ['content/image/default.html'] - if hasattr(self, 'position'): - templates.insert(0, 'content/image/%s.html' % self.position) - return render_to_string( - templates, - {'content': self}, - context_instance=kwargs.get('context'), - ) + templates = ["content/image/default.html"] + if hasattr(self, "position"): + templates.insert(0, "content/image/%s.html" % self.position) + + return AutoRenderTuple((templates, {"content": self})) def get_image(self): - type, separator, size = getattr(self, 'format', '').partition(':') + img_type, _, size = getattr(self, "format", "").partition(":") if not size: return self.image - thumbnailer = { - 'cropscale': feincms_thumbnail.CropscaleThumbnailer, - }.get(type, feincms_thumbnail.Thumbnailer) + thumbnailer = {"cropscale": feincms_thumbnail.CropscaleThumbnailer}.get( + img_type, feincms_thumbnail.Thumbnailer + ) return thumbnailer(self.image, size) @classmethod def initialize_type(cls, POSITION_CHOICES=None, FORMAT_CHOICES=None): if POSITION_CHOICES: models.CharField( - _('position'), + _("position"), max_length=10, choices=POSITION_CHOICES, - default=POSITION_CHOICES[0][0] - ).contribute_to_class(cls, 'position') + default=POSITION_CHOICES[0][0], + ).contribute_to_class(cls, "position") if FORMAT_CHOICES: models.CharField( - _('format'), + _("format"), max_length=64, choices=FORMAT_CHOICES, - default=FORMAT_CHOICES[0][0] - ).contribute_to_class(cls, 'format') + default=FORMAT_CHOICES[0][0], + ).contribute_to_class(cls, "format") diff --git a/feincms/content/medialibrary/models.py b/feincms/content/medialibrary/models.py index 43545662c..3c575e56c 100644 --- a/feincms/content/medialibrary/models.py +++ b/feincms/content/medialibrary/models.py @@ -1,79 +1,10 @@ -# ------------------------------------------------------------------------ -# coding=utf-8 -# ------------------------------------------------------------------------ +# flake8: noqa -from __future__ import absolute_import, unicode_literals +import warnings -from django.contrib import admin -from django.core.exceptions import ImproperlyConfigured -from django.db import models -from django.template.loader import render_to_string -from django.utils.translation import ugettext_lazy as _ -from feincms.admin.item_editor import FeinCMSInline -from feincms.module.medialibrary.fields import ContentWithMediaFile - - -class MediaFileContentInline(FeinCMSInline): - raw_id_fields = ('mediafile',) - radio_fields = {'type': admin.VERTICAL} - - -class MediaFileContent(ContentWithMediaFile): - """ - Rehashed, backwards-incompatible media file content which does not contain - the problems from v1 anymore. - - Create a media file content as follows:: - - from feincms.content.medialibrary.v2 import MediaFileContent - Page.create_content_type(MediaFileContent, TYPE_CHOICES=( - ('default', _('Default')), - ('lightbox', _('Lightbox')), - ('whatever', _('Whatever')), - )) - - For a media file of type 'image' and type 'lightbox', the following - templates are tried in order: - - * content/mediafile/image_lightbox.html - * content/mediafile/image.html - * content/mediafile/lightbox.html - * content/mediafile/default.html - - The context contains ``content`` and ``request`` (if available). - """ - - feincms_item_editor_inline = MediaFileContentInline - - class Meta: - abstract = True - verbose_name = _('media file') - verbose_name_plural = _('media files') - - @classmethod - def initialize_type(cls, TYPE_CHOICES=None): - if TYPE_CHOICES is None: - raise ImproperlyConfigured( - 'You have to set TYPE_CHOICES when' - ' creating a %s' % cls.__name__) - - cls.add_to_class( - 'type', - models.CharField( - _('type'), - max_length=20, - choices=TYPE_CHOICES, - default=TYPE_CHOICES[0][0], - ) - ) - - def render(self, **kwargs): - ctx = {'content': self} - ctx.update(kwargs) - return render_to_string([ - 'content/mediafile/%s_%s.html' % (self.mediafile.type, self.type), - 'content/mediafile/%s.html' % self.mediafile.type, - 'content/mediafile/%s.html' % self.type, - 'content/mediafile/default.html', - ], ctx, context_instance=kwargs.get('context')) +warnings.warn( + "Import MediaFileContent from feincms.module.medialibrary.contents.", + DeprecationWarning, + stacklevel=2, +) diff --git a/feincms/content/raw/models.py b/feincms/content/raw/models.py index 487c5a241..ca8068642 100644 --- a/feincms/content/raw/models.py +++ b/feincms/content/raw/models.py @@ -1,8 +1,6 @@ -from __future__ import absolute_import, unicode_literals - from django.db import models from django.utils.safestring import mark_safe -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import gettext_lazy as _ class RawContent(models.Model): @@ -13,12 +11,12 @@ class RawContent(models.Model): snippets too. """ - text = models.TextField(_('content'), blank=True) + text = models.TextField(_("content"), blank=True) class Meta: abstract = True - verbose_name = _('raw content') - verbose_name_plural = _('raw contents') + verbose_name = _("raw content") + verbose_name_plural = _("raw contents") def render(self, **kwargs): return mark_safe(self.text) diff --git a/feincms/content/richtext/models.py b/feincms/content/richtext/models.py index b72d46861..cd698ee69 100644 --- a/feincms/content/richtext/models.py +++ b/feincms/content/richtext/models.py @@ -1,76 +1,9 @@ -from __future__ import absolute_import, unicode_literals - -from django import forms -from django.core.exceptions import ImproperlyConfigured from django.db import models -from django.forms.util import ErrorList -from django.template.loader import render_to_string -from django.utils.html import escape -from django.utils.safestring import mark_safe -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import gettext_lazy as _ from feincms import settings -from feincms.admin.item_editor import ItemEditorForm from feincms.contrib.richtext import RichTextField -from feincms.utils import get_object - - -class RichTextContentAdminForm(ItemEditorForm): - #: If FEINCMS_TIDY_ALLOW_WARNINGS_OVERRIDE allows, we'll convert this into - # a checkbox so the user can choose whether to ignore HTML validation - # warnings instead of fixing them: - seen_tidy_warnings = forms.BooleanField( - required=False, - label=_("HTML Tidy"), - help_text=_("Ignore the HTML validation warnings"), - widget=forms.HiddenInput - ) - - def clean(self): - cleaned_data = super(RichTextContentAdminForm, self).clean() - - if settings.FEINCMS_TIDY_HTML: - text, errors, warnings = get_object( - settings.FEINCMS_TIDY_FUNCTION)(cleaned_data['text']) - - # Ick, but we need to be able to update text and seen_tidy_warnings - self.data = self.data.copy() - - # We always replace the HTML with the tidied version: - cleaned_data['text'] = text - self.data['%s-text' % self.prefix] = text - - if settings.FEINCMS_TIDY_SHOW_WARNINGS and (errors or warnings): - if settings.FEINCMS_TIDY_ALLOW_WARNINGS_OVERRIDE: - # Convert the ignore input from hidden to Checkbox so the - # user can change it: - self.fields['seen_tidy_warnings'].widget =\ - forms.CheckboxInput() - - if errors or not ( - settings.FEINCMS_TIDY_ALLOW_WARNINGS_OVERRIDE - and cleaned_data['seen_tidy_warnings']): - self._errors["text"] = ErrorList([mark_safe( - _( - "HTML validation produced %(count)d warnings." - " Please review the updated content below before" - " continuing: %(messages)s" - ) % { - "count": len(warnings) + len(errors), - "messages": '
  • %s
' % ( - "
  • ".join( - map(escape, errors + warnings))), - } - )]) - - # If we're allowed to ignore warnings and we don't have any - # errors we'll set our hidden form field to allow the user to - # ignore warnings on the next submit: - if (not errors - and settings.FEINCMS_TIDY_ALLOW_WARNINGS_OVERRIDE): - self.data["%s-seen_tidy_warnings" % self.prefix] = True - - return cleaned_data +from feincms.utils.tuple import AutoRenderTuple class RichTextContent(models.Model): @@ -86,57 +19,19 @@ class RichTextContent(models.Model): ``cleanse=True`` when calling ``create_content_type``. """ - form = RichTextContentAdminForm - feincms_item_editor_form = RichTextContentAdminForm - feincms_item_editor_context_processors = ( lambda x: settings.FEINCMS_RICHTEXT_INIT_CONTEXT, ) - feincms_item_editor_includes = { - 'head': [settings.FEINCMS_RICHTEXT_INIT_TEMPLATE], - } - - text = RichTextField(_('text'), blank=True) + feincms_item_editor_includes = {"head": [settings.FEINCMS_RICHTEXT_INIT_TEMPLATE]} class Meta: abstract = True - verbose_name = _('rich text') - verbose_name_plural = _('rich texts') + verbose_name = _("rich text") + verbose_name_plural = _("rich texts") def render(self, **kwargs): - return render_to_string( - 'content/richtext/default.html', - {'content': self}, - context_instance=kwargs.get('context')) - - def save(self, *args, **kwargs): - # TODO: Move this to the form? - if getattr(self, 'cleanse', None): - # Passes the rich text content as first argument because - # the passed callable has been converted into a bound method - self.text = self.cleanse(self.text) - - super(RichTextContent, self).save(*args, **kwargs) - save.alters_data = True + return AutoRenderTuple(("content/richtext/default.html", {"content": self})) @classmethod def initialize_type(cls, cleanse=None): - def to_instance_method(func): - def func_im(self, *args, **kwargs): - return func(*args, **kwargs) - return func_im - - if cleanse: - cls.cleanse = to_instance_method(cleanse) - - # TODO: Move this into somewhere more generic: - if settings.FEINCMS_TIDY_HTML: - # Make sure we can load the tidy function without dependency - # failures: - try: - get_object(settings.FEINCMS_TIDY_FUNCTION) - except ImportError as e: - raise ImproperlyConfigured( - "FEINCMS_TIDY_HTML is enabled but the HTML tidy function" - " %s could not be imported: %s" % ( - settings.FEINCMS_TIDY_FUNCTION, e)) + cls.add_to_class("text", RichTextField(_("text"), blank=True, cleanse=cleanse)) diff --git a/feincms/content/rss/models.py b/feincms/content/rss/models.py deleted file mode 100644 index 70353d1af..000000000 --- a/feincms/content/rss/models.py +++ /dev/null @@ -1,63 +0,0 @@ -from __future__ import absolute_import, unicode_literals - -import time - -from django.db import models -from django.utils import timezone -from django.utils.safestring import mark_safe -from django.utils.translation import ugettext_lazy as _ -from django.template.loader import render_to_string - -import feedparser - - -class RSSContent(models.Model): - """ - RSS feed inclusion content. - - This content requires a cronjob on the server, which runs - ``./manage.py update_rsscontent`` every couple of hours. - """ - - title = models.CharField( - _('title'), max_length=50, - help_text=_( - 'The rss field is updated several times a day.' - ' A change in the title will only be visible on the home page' - ' after the next feed update.')) - link = models.URLField(_('link')) - rendered_content = models.TextField( - _('pre-rendered content'), blank=True, editable=False) - last_updated = models.DateTimeField( - _('last updated'), blank=True, null=True) - max_items = models.IntegerField(_('max. items'), default=5) - - class Meta: - abstract = True - verbose_name = _('RSS feed') - verbose_name_plural = _('RSS feeds') - - def render(self, **kwargs): - return mark_safe(self.rendered_content) - - def cache_content(self, date_format=None, save=True): - feed = feedparser.parse(self.link) - entries = feed['entries'][:self.max_items] - if date_format: - for entry in entries: - entry.updated = time.strftime( - date_format, entry.updated_parsed) - - self.rendered_content = render_to_string( - 'content/rss/content.html', - { - 'feed_title': self.title, - 'feed_link': feed['feed'].get('link'), - 'entries': entries, - }, - ) - self.last_updated = timezone.now() - - if save: - self.save() - cache_content.alters_data = True diff --git a/feincms/content/section/models.py b/feincms/content/section/models.py index 88516290a..48f344cc7 100644 --- a/feincms/content/section/models.py +++ b/feincms/content/section/models.py @@ -1,22 +1,20 @@ -from __future__ import absolute_import, unicode_literals - from django.conf import settings as django_settings from django.contrib import admin from django.core.exceptions import ImproperlyConfigured from django.db import models -from django.template.loader import render_to_string -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import gettext_lazy as _ from feincms import settings from feincms.admin.item_editor import FeinCMSInline from feincms.contrib.richtext import RichTextField from feincms.module.medialibrary.fields import MediaFileForeignKey from feincms.module.medialibrary.models import MediaFile +from feincms.utils.tuple import AutoRenderTuple class SectionContentInline(FeinCMSInline): - raw_id_fields = ('mediafile',) - radio_fields = {'type': admin.VERTICAL} + raw_id_fields = ("mediafile",) + radio_fields = {"type": admin.VERTICAL} class SectionContent(models.Model): @@ -28,38 +26,46 @@ class SectionContent(models.Model): feincms_item_editor_context_processors = ( lambda x: settings.FEINCMS_RICHTEXT_INIT_CONTEXT, ) - feincms_item_editor_includes = { - 'head': [settings.FEINCMS_RICHTEXT_INIT_TEMPLATE], - } + feincms_item_editor_includes = {"head": [settings.FEINCMS_RICHTEXT_INIT_TEMPLATE]} - title = models.CharField(_('title'), max_length=200, blank=True) - richtext = RichTextField(_('text'), blank=True) + title = models.CharField(_("title"), max_length=200, blank=True) + richtext = RichTextField(_("text"), blank=True) mediafile = MediaFileForeignKey( - MediaFile, verbose_name=_('media file'), - related_name='+', blank=True, null=True) + MediaFile, + on_delete=models.CASCADE, + verbose_name=_("media file"), + related_name="+", + blank=True, + null=True, + ) class Meta: abstract = True - verbose_name = _('section') - verbose_name_plural = _('sections') + verbose_name = _("section") + verbose_name_plural = _("sections") @classmethod def initialize_type(cls, TYPE_CHOICES=None, cleanse=None): - if 'feincms.module.medialibrary' not in django_settings.INSTALLED_APPS: + if "feincms.module.medialibrary" not in django_settings.INSTALLED_APPS: raise ImproperlyConfigured( - 'You have to add \'feincms.module.medialibrary\' to your' - ' INSTALLED_APPS before creating a %s' % cls.__name__) + "You have to add 'feincms.module.medialibrary' to your" + " INSTALLED_APPS before creating a %s" % cls.__name__ + ) if TYPE_CHOICES is None: raise ImproperlyConfigured( - 'You need to set TYPE_CHOICES when creating a' - ' %s' % cls.__name__) - - cls.add_to_class('type', models.CharField( - _('type'), - max_length=10, choices=TYPE_CHOICES, - default=TYPE_CHOICES[0][0] - )) + "You need to set TYPE_CHOICES when creating a %s" % cls.__name__ + ) + + cls.add_to_class( + "type", + models.CharField( + _("type"), + max_length=10, + choices=TYPE_CHOICES, + default=TYPE_CHOICES[0][0], + ), + ) if cleanse: cls.cleanse = cleanse @@ -67,27 +73,28 @@ def initialize_type(cls, TYPE_CHOICES=None, cleanse=None): @classmethod def get_queryset(cls, filter_args): # Explicitly add nullable FK mediafile to minimize the DB query count - return cls.objects.select_related('parent', 'mediafile').filter( - filter_args) + return cls.objects.select_related("parent", "mediafile").filter(filter_args) def render(self, **kwargs): if self.mediafile: mediafile_type = self.mediafile.type else: - mediafile_type = 'nomedia' - - return render_to_string( - [ - 'content/section/%s_%s.html' % (mediafile_type, self.type), - 'content/section/%s.html' % mediafile_type, - 'content/section/%s.html' % self.type, - 'content/section/default.html', - ], - {'content': self}, + mediafile_type = "nomedia" + + return AutoRenderTuple( + ( + [ + f"content/section/{mediafile_type}_{self.type}.html", + "content/section/%s.html" % mediafile_type, + "content/section/%s.html" % self.type, + "content/section/default.html", + ], + {"content": self}, + ) ) def save(self, *args, **kwargs): - if getattr(self, 'cleanse', None): + if getattr(self, "cleanse", None): try: # Passes the rich text content as first argument because # the passed callable has been converted into a bound method @@ -97,5 +104,6 @@ def save(self, *args, **kwargs): # content instance along self.richtext = self.cleanse.im_func(self.richtext) - super(SectionContent, self).save(*args, **kwargs) + super().save(*args, **kwargs) + save.alters_data = True diff --git a/feincms/content/template/models.py b/feincms/content/template/models.py index d04080e5b..57e99ab70 100644 --- a/feincms/content/template/models.py +++ b/feincms/content/template/models.py @@ -1,92 +1,34 @@ -from __future__ import absolute_import, unicode_literals - -import os - from django.db import models -from django.template.loader import ( - Context, Template, TemplateDoesNotExist, find_template_loader) -from django.utils.translation import ugettext_lazy as _ - - -DEFAULT_TEMPLATE_LOADERS = ( - 'django.template.loaders.filesystem.Loader', - 'django.template.loaders.app_directories.Loader', -) - - -class TemplateChoices(object): - def __init__(self, template_loaders): - self.template_loaders = template_loaders - - def __iter__(self): - seen = set() - - for loader in self.template_loaders: - for basepath in loader.get_template_sources('.'): - path = os.path.join(basepath, 'content', 'template') - try: - templates = os.listdir(path) - except (OSError, IOError): - continue - - for template in templates: - if template in seen: - continue - if template.endswith(('~', '.tmp')): - continue - seen.add(template) +from django.utils.translation import gettext_lazy as _ - return ((t, t) for t in sorted(seen)) +from feincms.content.raw.models import RawContent # noqa +from feincms.content.richtext.models import RichTextContent # noqa +from feincms.utils.tuple import AutoRenderTuple class TemplateContent(models.Model): """ - This content type scans all template folders for files in the - ``content/template/`` folder and lets the website administrator select - any template from a set of provided choices. - - The templates aren't restricted in any way. + Pass a list of templates when creating this content type. It uses the + default template system:: + + Page.create_content_type(TemplateContent, TEMPLATES=[ + ('content/template/something1.html', 'something'), + ('content/template/something2.html', 'something else'), + ('base.html', 'makes no sense'), + ]) """ class Meta: abstract = True - verbose_name = _('template content') - verbose_name_plural = _('template contents') + verbose_name = _("template content") + verbose_name_plural = _("template contents") @classmethod - def initialize_type(cls, TEMPLATE_LOADERS=DEFAULT_TEMPLATE_LOADERS): - cls.template_loaders = [ - find_template_loader(loader) - for loader in TEMPLATE_LOADERS if loader] - - cls.add_to_class('filename', models.CharField( - _('template'), max_length=100, - choices=TemplateChoices(cls.template_loaders))) + def initialize_type(cls, TEMPLATES): + cls.add_to_class( + "template", + models.CharField(_("template"), max_length=100, choices=TEMPLATES), + ) def render(self, **kwargs): - context = kwargs.pop('context', None) - name = 'content/template/%s' % self.filename - - for loader in self.template_loaders: - try: - template, display_name = loader.load_template(name) - except TemplateDoesNotExist: - continue - - if not hasattr(template, 'render'): - template = Template(template, name=name) - - if context: - ctx = context - ctx.update(dict(content=self, **kwargs)) - else: - ctx = Context(dict(content=self, **kwargs)) - - result = template.render(ctx) - - if context: - context.pop() - - return result - - return '' + return AutoRenderTuple((self.template, {"content": self})) diff --git a/feincms/content/video/models.py b/feincms/content/video/models.py index b650aa21a..e6f67da70 100644 --- a/feincms/content/video/models.py +++ b/feincms/content/video/models.py @@ -1,10 +1,9 @@ -from __future__ import absolute_import, unicode_literals - import re from django.db import models -from django.template.loader import render_to_string -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import gettext_lazy as _ + +from feincms.utils.tuple import AutoRenderTuple class VideoContent(models.Model): @@ -19,38 +18,43 @@ class VideoContent(models.Model): """ PORTALS = ( - ('youtube', re.compile(r'youtube'), lambda url: { - 'v': re.search(r'([?&]v=|./././)([^#&]+)', url).group(2), - }), - ('vimeo', re.compile(r'vimeo'), lambda url: { - 'id': re.search(r'/(\d+)', url).group(1), - }), - ('sf', re.compile(r'sf\.tv'), lambda url: { - 'id': re.search(r'/([a-z0-9\-]+)', url).group(1), - }), + ( + "youtube", + re.compile(r"youtube"), + lambda url: {"v": re.search(r"([?&]v=|./././)([^#&]+)", url).group(2)}, + ), + ( + "vimeo", + re.compile(r"vimeo"), + lambda url: {"id": re.search(r"/(\d+)", url).group(1)}, + ), + ( + "sf", + re.compile(r"sf\.tv"), + lambda url: {"id": re.search(r"/([a-z0-9\-]+)", url).group(1)}, + ), ) video = models.URLField( - _('video link'), + _("video link"), help_text=_( - 'This should be a link to a youtube or vimeo video,' - ' i.e.: http://www.youtube.com/watch?v=zmj1rpzDRZ0')) + "This should be a link to a youtube or vimeo video," + " i.e.: http://www.youtube.com/watch?v=zmj1rpzDRZ0" + ), + ) class Meta: abstract = True - verbose_name = _('video') - verbose_name_plural = _('videos') + verbose_name = _("video") + verbose_name_plural = _("videos") def get_context_dict(self): "Extend this if you need more variables passed to template" - return {'content': self, 'portal': 'unknown'} + return {"content": self, "portal": "unknown"} - def get_templates(self, portal='unknown'): + def get_templates(self, portal="unknown"): "Extend/override this if you want to modify the templates used" - return [ - 'content/video/%s.html' % portal, - 'content/video/unknown.html', - ] + return ["content/video/%s.html" % portal, "content/video/unknown.html"] def ctx_for_video(self, vurl): "Get a context dict for a given video URL" @@ -59,16 +63,12 @@ def ctx_for_video(self, vurl): if match.search(vurl): try: ctx.update(context_fn(vurl)) - ctx['portal'] = portal + ctx["portal"] = portal break except AttributeError: continue return ctx def render(self, **kwargs): - context_instance = kwargs.get('context') ctx = self.ctx_for_video(self.video) - return render_to_string( - self.get_templates(ctx['portal']), - ctx, - context_instance=context_instance) + return AutoRenderTuple((self.get_templates(ctx["portal"]), ctx)) diff --git a/feincms/contents.py b/feincms/contents.py new file mode 100644 index 000000000..dd31f2f18 --- /dev/null +++ b/feincms/contents.py @@ -0,0 +1,4 @@ +from feincms.content.filer.models import * # noqa +from feincms.content.raw.models import RawContent # noqa +from feincms.content.richtext.models import RichTextContent # noqa +from feincms.content.template.models import TemplateContent # noqa diff --git a/feincms/context_processors.py b/feincms/context_processors.py index 3f5c9b1a7..30bf7de70 100644 --- a/feincms/context_processors.py +++ b/feincms/context_processors.py @@ -7,8 +7,6 @@ def add_page_if_missing(request): """ try: - return { - 'feincms_page': Page.objects.for_request(request, best_match=True), - } + return {"feincms_page": Page.objects.for_request(request, best_match=True)} except Page.DoesNotExist: return {} diff --git a/feincms/contrib/fields.py b/feincms/contrib/fields.py index f69b3baa9..38f13a714 100644 --- a/feincms/contrib/fields.py +++ b/feincms/contrib/fields.py @@ -1,12 +1,9 @@ -from __future__ import absolute_import, unicode_literals - import json import logging from django import forms -from django.db import models from django.core.serializers.json import DjangoJSONEncoder -from django.utils import six +from django.db import models class JSONFormField(forms.fields.CharField): @@ -14,7 +11,7 @@ def clean(self, value, *args, **kwargs): # It seems that sometimes we receive dict objects here, not only # strings. Partial form validation maybe? if value: - if isinstance(value, six.string_types): + if isinstance(value, str): try: value = json.loads(value) except ValueError: @@ -27,10 +24,10 @@ def clean(self, value, *args, **kwargs): except ValueError: raise forms.ValidationError("Invalid JSON data!") - return super(JSONFormField, self).clean(value, *args, **kwargs) + return super().clean(value, *args, **kwargs) -class JSONField(six.with_metaclass(models.SubfieldBase, models.TextField)): +class JSONField(models.TextField): """ TextField which transparently serializes/unserializes JSON objects @@ -45,8 +42,7 @@ def to_python(self, value): if isinstance(value, dict): return value - elif (isinstance(value, six.string_types) - or isinstance(value, six.binary_type)): + elif isinstance(value, str) or isinstance(value, bytes): # Avoid asking the JSON decoder to handle empty values: if not value: return {} @@ -55,12 +51,16 @@ def to_python(self, value): return json.loads(value) except ValueError: logging.getLogger("feincms.contrib.fields").exception( - "Unable to deserialize store JSONField data: %s", value) + "Unable to deserialize store JSONField data: %s", value + ) return {} else: assert value is None return {} + def from_db_value(self, value, expression, connection, context=None): + return self.to_python(value) + def get_prep_value(self, value): """Convert our JSON object to a string before we save""" return self._flatten_value(value) @@ -85,18 +85,6 @@ def _flatten_value(self, value): if isinstance(value, dict): value = json.dumps(value, cls=DjangoJSONEncoder) - assert isinstance(value, six.string_types) + assert isinstance(value, str) return value - - -try: - from south.modelsinspector import add_introspection_rules - - JSONField_introspection_rule = ((JSONField,), [], {},) - - add_introspection_rules( - rules=[JSONField_introspection_rule], - patterns=["^feincms\.contrib\.fields"]) -except ImportError: - pass diff --git a/feincms/contrib/preview/urls.py b/feincms/contrib/preview/urls.py index 83bc5e4d8..bf9f21a7d 100644 --- a/feincms/contrib/preview/urls.py +++ b/feincms/contrib/preview/urls.py @@ -1,10 +1,8 @@ -from django.conf.urls import patterns, url +from django.urls import re_path from feincms.contrib.preview.views import PreviewHandler -urlpatterns = patterns( - '', - url(r'^(.*)/_preview/(\d+)/$', PreviewHandler.as_view(), - name='feincms_preview'), -) +urlpatterns = [ + re_path(r"^(.*)/_preview/(\d+)/$", PreviewHandler.as_view(), name="feincms_preview") +] diff --git a/feincms/contrib/preview/views.py b/feincms/contrib/preview/views.py index c8486d651..08b3e5aaa 100644 --- a/feincms/contrib/preview/views.py +++ b/feincms/contrib/preview/views.py @@ -1,9 +1,7 @@ -from __future__ import absolute_import, unicode_literals - from django.http import Http404 from django.shortcuts import get_object_or_404 -from feincms.views.cbv.views import Handler +from feincms.views import Handler class PreviewHandler(Handler): @@ -18,8 +16,13 @@ class PreviewHandler(Handler): def get_object(self): """Get the page by the id in the url here instead.""" - page = get_object_or_404(self.page_model, pk=self.args[1]) + # This might happen when there is a preview request for + # a page that doesn't exist, and now FeinCMS wants to + # show a 404 page via FEINCMS_CMS_404_PAGE. + if len(self.args) < 2: + return super().get_object() + page = get_object_or_404(self.page_model, pk=self.args[1]) # Remove _preview/42/ from URL, the rest of the handler code should not # know that anything about previewing. Handler.prepare will still raise # a 404 if the extra_path isn't consumed by any content type @@ -29,9 +32,7 @@ def get_object(self): def handler(self, request, *args, **kwargs): if not request.user.is_staff: - raise Http404('Not found (not allowed)') - response = super(PreviewHandler, self).handler( - request, *args, **kwargs) - response['Cache-Control'] =\ - 'no-cache, must-revalidate, no-store, private' + raise Http404("Not found (not allowed)") + response = super().handler(request, *args, **kwargs) + response["Cache-Control"] = "no-cache, must-revalidate, no-store, private" return response diff --git a/feincms/contrib/richtext.py b/feincms/contrib/richtext.py index 5ad17559c..5a6a7da19 100644 --- a/feincms/contrib/richtext.py +++ b/feincms/contrib/richtext.py @@ -1,19 +1,20 @@ -from __future__ import absolute_import, unicode_literals - from django import forms from django.db import models class RichTextFormField(forms.fields.CharField): def __init__(self, *args, **kwargs): - super(RichTextFormField, self).__init__(*args, **kwargs) - css_class = self.widget.attrs.get('class', '') - css_class += ' item-richtext' - self.widget.attrs['class'] = css_class + self.cleanse = kwargs.pop("cleanse", None) + super().__init__(*args, **kwargs) + css_class = self.widget.attrs.get("class", "") + css_class += " item-richtext" + self.widget.attrs["class"] = css_class def clean(self, value): - # TODO add cleansing here? - return super(RichTextFormField, self).clean(value) + value = super().clean(value) + if self.cleanse: + value = self.cleanse(value) + return value class RichTextField(models.TextField): @@ -21,18 +22,10 @@ class RichTextField(models.TextField): Drop-in replacement for Django's ``models.TextField`` which allows editing rich text instead of plain text in the item editor. """ - def formfield(self, form_class=RichTextFormField, **kwargs): - return super(RichTextField, self).formfield( - form_class=form_class, **kwargs) - -try: - from south.modelsinspector import add_introspection_rules - - RichTextField_introspection_rule = ((RichTextField,), [], {},) + def __init__(self, *args, **kwargs): + self.cleanse = kwargs.pop("cleanse", None) + super().__init__(*args, **kwargs) - add_introspection_rules( - rules=[RichTextField_introspection_rule], - patterns=["^feincms\.contrib\.richtext"]) -except ImportError: - pass + def formfield(self, form_class=RichTextFormField, **kwargs): + return super().formfield(form_class=form_class, cleanse=self.cleanse, **kwargs) diff --git a/feincms/contrib/tagging.py b/feincms/contrib/tagging.py index b730f7ebe..6c29eb727 100644 --- a/feincms/contrib/tagging.py +++ b/feincms/contrib/tagging.py @@ -1,5 +1,4 @@ # ------------------------------------------------------------------------ -# coding=utf-8 # ------------------------------------------------------------------------ # FeinCMS django-tagging support. To add tagging to your (page) model, # simply do a @@ -8,32 +7,31 @@ # tagging.tag_model(Page) # ------------------------------------------------------------------------ -from __future__ import absolute_import, unicode_literals from django import forms from django.contrib.admin.widgets import FilteredSelectMultiple from django.db.models.signals import pre_save -from django.utils import six -from django.utils.translation import ugettext_lazy as _ - -from tagging import AlreadyRegistered +from django.utils.translation import gettext_lazy as _ from tagging.fields import TagField from tagging.models import Tag +from tagging.registry import AlreadyRegistered, register as tagging_register from tagging.utils import parse_tag_input # ------------------------------------------------------------------------ def taglist_to_string(taglist): - retval = '' + retval = "" if len(taglist) >= 1: taglist.sort() - retval = ','.join(taglist) + retval = ",".join(taglist) return retval + # ------------------------------------------------------------------------ # The following is lifted from: # http://code.google.com/p/django-tagging/issues/detail?id=189 + """ TagSelectField @@ -52,32 +50,36 @@ def clean(self, value): return taglist_to_string(list(value)) +class Tag_formatvalue_mixin: + def format_value(self, value): + value = parse_tag_input(value or "") + return super().format_value(value) + + +class fv_FilteredSelectMultiple(Tag_formatvalue_mixin, FilteredSelectMultiple): + pass + + +class fv_SelectMultiple(Tag_formatvalue_mixin, forms.SelectMultiple): + pass + + class TagSelectField(TagField): def __init__(self, filter_horizontal=False, *args, **kwargs): - super(TagSelectField, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) self.filter_horizontal = filter_horizontal def formfield(self, **defaults): if self.filter_horizontal: - widget = FilteredSelectMultiple( - self.verbose_name, is_stacked=False) + widget = fv_FilteredSelectMultiple(self.verbose_name, is_stacked=False) else: - widget = forms.SelectMultiple() + widget = fv_SelectMultiple() - def _render(name, value, attrs=None, *args, **kwargs): - value = parse_tag_input(value) - return type(widget).render( - widget, name, value, attrs, *args, **kwargs) - widget.render = _render - defaults['widget'] = widget - choices = [( - six.text_type(t), - six.text_type(t)) for t in Tag.objects.all()] - return TagSelectFormField( - choices=choices, required=not self.blank, **defaults) + defaults["widget"] = widget + choices = [(str(t), str(t)) for t in Tag.objects.all()] + return TagSelectFormField(choices=choices, required=not self.blank, **defaults) -# ------------------------------------------------------------------------ # ------------------------------------------------------------------------ def pre_save_handler(sender, instance, **kwargs): """ @@ -89,9 +91,15 @@ def pre_save_handler(sender, instance, **kwargs): # ------------------------------------------------------------------------ -def tag_model(cls, admin_cls=None, field_name='tags', sort_tags=False, - select_field=False, auto_add_admin_field=True, - admin_list_display=True): +def tag_model( + cls, + admin_cls=None, + field_name="tags", + sort_tags=False, + select_field=False, + auto_add_admin_field=True, + admin_list_display=True, +): """ tag_model accepts a number of named parameters: @@ -109,16 +117,18 @@ def tag_model(cls, admin_cls=None, field_name='tags', sort_tags=False, auto_add_admin_field If True, attempts to add the tag field to the admin class. """ - from tagging import register as tagging_register - cls.add_to_class(field_name, ( - TagSelectField if select_field else TagField - )(field_name.capitalize(), blank=True)) + cls.add_to_class( + field_name, + (TagSelectField if select_field else TagField)( + field_name.capitalize(), blank=True + ), + ) # use another name for the tag descriptor # See http://code.google.com/p/django-tagging/issues/detail?id=95 for the # reason why try: - tagging_register(cls, tag_descriptor_attr='tagging_' + field_name) + tagging_register(cls, tag_descriptor_attr="tagging_" + field_name) except AlreadyRegistered: return @@ -127,13 +137,11 @@ def tag_model(cls, admin_cls=None, field_name='tags', sort_tags=False, admin_cls.list_display.append(field_name) admin_cls.list_filter.append(field_name) - if auto_add_admin_field and hasattr( - admin_cls, 'add_extension_options'): - admin_cls.add_extension_options(_('Tagging'), { - 'fields': (field_name,) - }) + if auto_add_admin_field and hasattr(admin_cls, "add_extension_options"): + admin_cls.add_extension_options(_("Tagging"), {"fields": (field_name,)}) if sort_tags: pre_save.connect(pre_save_handler, sender=cls) + # ------------------------------------------------------------------------ diff --git a/feincms/default_settings.py b/feincms/default_settings.py index 8f97158a2..488bf673f 100644 --- a/feincms/default_settings.py +++ b/feincms/default_settings.py @@ -1,5 +1,4 @@ # ------------------------------------------------------------------------ -# coding=utf-8 # ------------------------------------------------------------------------ """ Default settings for FeinCMS @@ -8,116 +7,76 @@ ``settings.py`` file. """ -from __future__ import absolute_import, unicode_literals - -from os.path import join - from django.conf import settings -# ------------------------------------------------------------------------ -# Settings for Generic Content # e.g. 'uploads' if you would prefer /uploads/imagecontent/test.jpg # to /imagecontent/test.jpg. -FEINCMS_UPLOAD_PREFIX = getattr( - settings, - 'FEINCMS_UPLOAD_PREFIX', - '') +FEINCMS_UPLOAD_PREFIX = getattr(settings, "FEINCMS_UPLOAD_PREFIX", "") # ------------------------------------------------------------------------ # Settings for MediaLibrary #: Local path to newly uploaded media files FEINCMS_MEDIALIBRARY_UPLOAD_TO = getattr( - settings, - 'FEINCMS_MEDIALIBRARY_UPLOAD_TO', - 'medialibrary/%Y/%m/') + settings, "FEINCMS_MEDIALIBRARY_UPLOAD_TO", "medialibrary/%Y/%m/" +) #: Thumbnail function for suitable mediafiles. Only receives the media file #: and should return a thumbnail URL (or nothing). FEINCMS_MEDIALIBRARY_THUMBNAIL = getattr( settings, - 'FEINCMS_MEDIALIBRARY_THUMBNAIL', - 'feincms.module.medialibrary.thumbnail.default_admin_thumbnail') + "FEINCMS_MEDIALIBRARY_THUMBNAIL", + "feincms.module.medialibrary.thumbnail.default_admin_thumbnail", +) # ------------------------------------------------------------------------ # Settings for RichText FEINCMS_RICHTEXT_INIT_TEMPLATE = getattr( settings, - 'FEINCMS_RICHTEXT_INIT_TEMPLATE', - 'admin/content/richtext/init_tinymce.html') + "FEINCMS_RICHTEXT_INIT_TEMPLATE", + "admin/content/richtext/init_tinymce4.html", +) FEINCMS_RICHTEXT_INIT_CONTEXT = getattr( settings, - 'FEINCMS_RICHTEXT_INIT_CONTEXT', { - 'TINYMCE_JS_URL': join(settings.MEDIA_URL, 'js/tiny_mce/tiny_mce.js'), - 'TINYMCE_DOMAIN': None, - 'TINYMCE_CONTENT_CSS_URL': None, - 'TINYMCE_LINK_LIST_URL': None - } + "FEINCMS_RICHTEXT_INIT_CONTEXT", + { + "TINYMCE_JS_URL": "https://cdnjs.cloudflare.com/ajax/libs/tinymce/4.9.11/tinymce.min.js", # noqa + "TINYMCE_DOMAIN": None, + "TINYMCE_CONTENT_CSS_URL": None, + "TINYMCE_LINK_LIST_URL": None, + }, ) -# ------------------------------------------------------------------------ -# Admin media settings - -#: avoid jQuery conflicts -- scripts should use feincms.jQuery instead of $ -FEINCMS_JQUERY_NO_CONFLICT = getattr( - settings, - 'FEINCMS_JQUERY_NO_CONFLICT', - False) - # ------------------------------------------------------------------------ # Settings for the page module #: Include ancestors in filtered tree editor lists FEINCMS_TREE_EDITOR_INCLUDE_ANCESTORS = getattr( - settings, - 'FEINCMS_TREE_EDITOR_INCLUDE_ANCESTORS', - False) - -#: Show frontend-editing button? -FEINCMS_FRONTEND_EDITING = getattr( - settings, - 'FEINCMS_FRONTEND_EDITING', - False) + settings, "FEINCMS_TREE_EDITOR_INCLUDE_ANCESTORS", False +) #: Enable checking of object level permissions. Note that if this option is #: enabled, you must plug in an authentication backend that actually does #: implement object level permissions or no page will be editable. FEINCMS_TREE_EDITOR_OBJECT_PERMISSIONS = getattr( - settings, - 'FEINCMS_TREE_EDITOR_OBJECT_PERMISSIONS', - False) + settings, "FEINCMS_TREE_EDITOR_OBJECT_PERMISSIONS", False +) #: When enabled, the page module is automatically registered with Django's #: default admin site (this is activated by default). -FEINCMS_USE_PAGE_ADMIN = getattr( - settings, - 'FEINCMS_USE_PAGE_ADMIN', - True) +FEINCMS_USE_PAGE_ADMIN = getattr(settings, "FEINCMS_USE_PAGE_ADMIN", True) -#: app_label.model_name as per django.db.models.get_model. +#: app_label.model_name as per apps.get_model. #: defaults to page.Page FEINCMS_DEFAULT_PAGE_MODEL = getattr( - settings, - 'FEINCMS_DEFAULT_PAGE_MODEL', - 'page.Page') - -# ------------------------------------------------------------------------ -# Various settings - -#: Run the weak replacement for a real database migration solution? -FEINCMS_CHECK_DATABASE_SCHEMA = getattr( - settings, - 'FEINCMS_CHECK_DATABASE_SCHEMA', - True) + settings, "FEINCMS_DEFAULT_PAGE_MODEL", "page.Page" +) # ------------------------------------------------------------------------ #: Allow random gunk after a valid page? -FEINCMS_ALLOW_EXTRA_PATH = getattr( - settings, - 'FEINCMS_ALLOW_EXTRA_PATH', - False) +FEINCMS_ALLOW_EXTRA_PATH = getattr(settings, "FEINCMS_ALLOW_EXTRA_PATH", False) # ------------------------------------------------------------------------ #: How to switch languages. @@ -125,37 +84,7 @@ #: and overwrites whatever was set before. #: * ``'EXPLICIT'``: The language set has priority, may only be overridden #: by explicitely a language with ``?set_language=xx``. -FEINCMS_TRANSLATION_POLICY = getattr( - settings, - 'FEINCMS_TRANSLATION_POLICY', - 'STANDARD') - -# ------------------------------------------------------------------------ -# Settings for HTML validation - -#: If True, HTML will be run through a tidy function before saving: -FEINCMS_TIDY_HTML = getattr( - settings, - 'FEINCMS_TIDY_HTML', - False) -#: If True, displays form validation errors so the user can see how their -#: HTML has been changed: -FEINCMS_TIDY_SHOW_WARNINGS = getattr( - settings, - 'FEINCMS_TIDY_SHOW_WARNINGS', - True) -#: If True, users will be allowed to ignore HTML warnings (errors are always -#: blocked): -FEINCMS_TIDY_ALLOW_WARNINGS_OVERRIDE = getattr( - settings, - 'FEINCMS_TIDY_ALLOW_WARNINGS_OVERRIDE', - True) -#: Name of the tidy function - anything which takes ``(html)`` and returns -#: ``(html, errors, warnings)`` can be used: -FEINCMS_TIDY_FUNCTION = getattr( - settings, - 'FEINCMS_TIDY_FUNCTION', - 'feincms.utils.html.tidy.tidy_html') +FEINCMS_TRANSLATION_POLICY = getattr(settings, "FEINCMS_TRANSLATION_POLICY", "STANDARD") # ------------------------------------------------------------------------ #: Makes the page handling mechanism try to find a cms page with that @@ -163,43 +92,54 @@ #: customised cms-styled error pages. Do not go overboard, this should #: be as simple and as error resistant as possible, so refrain from #: deeply nested error pages or advanced content types. -FEINCMS_CMS_404_PAGE = getattr( - settings, - 'FEINCMS_CMS_404_PAGE', - None) +FEINCMS_CMS_404_PAGE = getattr(settings, "FEINCMS_CMS_404_PAGE", None) # ------------------------------------------------------------------------ #: When uploading files to the media library, replacing an existing entry, #: try to save the new file under the old file name in order to keep the #: media file path (and thus the media url) constant. #: Experimental, this might not work with all storage backends. -FEINCMS_MEDIAFILE_OVERWRITE = getattr( - settings, - 'FEINCMS_MEDIAFILE_OVERWRITE', - False) +FEINCMS_MEDIAFILE_OVERWRITE = getattr(settings, "FEINCMS_MEDIAFILE_OVERWRITE", False) # ------------------------------------------------------------------------ #: Prefix for thumbnails. Set this to something non-empty to separate thumbs #: from uploads. The value should end with a slash, but this is not enforced. -FEINCMS_THUMBNAIL_DIR = getattr( - settings, - 'FEINCMS_THUMBNAIL_DIR', - '_thumbs/') +FEINCMS_THUMBNAIL_DIR = getattr(settings, "FEINCMS_THUMBNAIL_DIR", "_thumbs/") + +# ------------------------------------------------------------------------ +#: feincms_thumbnail template filter library cache timeout. The default is to +#: not cache anything for backwards compatibility. +FEINCMS_THUMBNAIL_CACHE_TIMEOUT = getattr( + settings, "FEINCMS_THUMBNAIL_CACHE_TIMEOUT", 0 +) # ------------------------------------------------------------------------ #: Prevent changing template within admin for pages which have been #: allocated a Template with singleton=True -- template field will become #: read-only for singleton pages. FEINCMS_SINGLETON_TEMPLATE_CHANGE_ALLOWED = getattr( - settings, - 'FEINCMS_SINGLETON_TEMPLATE_CHANGE_ALLOWED', - False) + settings, "FEINCMS_SINGLETON_TEMPLATE_CHANGE_ALLOWED", False +) #: Prevent admin page deletion for pages which have been allocated a #: Template with singleton=True FEINCMS_SINGLETON_TEMPLATE_DELETION_ALLOWED = getattr( - settings, - 'FEINCMS_SINGLETON_TEMPLATE_DELETION_ALLOWED', - False) + settings, "FEINCMS_SINGLETON_TEMPLATE_DELETION_ALLOWED", False +) + +# ------------------------------------------------------------------------ +#: Filter languages available for front end users to this set. This allows +#: to have languages not yet ready for prime time while being able to access +#: those pages in the admin backend. +FEINCMS_FRONTEND_LANGUAGES = getattr(settings, "FEINCMS_FRONTEND_LANGUAGES", None) + +# ------------------------------------------------------------------------ + +# ------------------------------------------------------------------------ +#: Attempt to get translations of MediaFile objects. If `False`, FeinCMS will +#: instead just output the file name. +FEINCMS_MEDIAFILE_TRANSLATIONS = getattr( + settings, "FEINCMS_MEDIAFILE_TRANSLATIONS", True +) # ------------------------------------------------------------------------ diff --git a/feincms/extensions/__init__.py b/feincms/extensions/__init__.py new file mode 100644 index 000000000..f35d3ed49 --- /dev/null +++ b/feincms/extensions/__init__.py @@ -0,0 +1,14 @@ +from .base import ( + Extension, + ExtensionModelAdmin, + ExtensionsMixin, + prefetch_modeladmin_get_queryset, +) + + +__all__ = ( + "ExtensionsMixin", + "Extension", + "ExtensionModelAdmin", + "prefetch_modeladmin_get_queryset", +) diff --git a/feincms/extensions.py b/feincms/extensions/base.py similarity index 63% rename from feincms/extensions.py rename to feincms/extensions/base.py index 5a31ac648..c1b84b382 100644 --- a/feincms/extensions.py +++ b/feincms/extensions/base.py @@ -2,19 +2,16 @@ Base types for extensions refactor """ -from __future__ import absolute_import, unicode_literals - -from functools import wraps import inspect +from functools import wraps from django.contrib import admin from django.core.exceptions import ImproperlyConfigured -from django.utils import six from feincms.utils import get_object -class ExtensionsMixin(object): +class ExtensionsMixin: @classmethod def register_extensions(cls, *extensions): """ @@ -27,7 +24,7 @@ def register_extensions(cls, *extensions): sufficient. """ - if not hasattr(cls, '_extensions'): + if not hasattr(cls, "_extensions"): cls._extensions = [] cls._extensions_seen = [] @@ -40,47 +37,48 @@ def register_extensions(cls, *extensions): if inspect.isclass(ext) and issubclass(ext, Extension): extension = ext - elif isinstance(ext, six.string_types): + elif isinstance(ext, str): try: extension = get_object(ext) except (AttributeError, ImportError, ValueError): if not extension: raise ImproperlyConfigured( - '%s is not a valid extension for %s' % ( - ext, cls.__name__)) + f"{ext} is not a valid extension for {cls.__name__}" + ) - if hasattr(extension, 'Extension'): + if hasattr(extension, "Extension"): extension = extension.Extension - elif hasattr(extension, 'register'): + elif hasattr(extension, "register"): extension = extension.register - elif hasattr(extension, '__call__'): + elif hasattr(extension, "__call__"): pass else: raise ImproperlyConfigured( - '%s is not a valid extension for %s' % ( - ext, cls.__name__)) + f"{ext} is not a valid extension for {cls.__name__}" + ) if extension in cls._extensions_seen: continue cls._extensions_seen.append(extension) - if hasattr(extension, 'handle_model'): + if hasattr(extension, "handle_model"): cls._extensions.append(extension(cls)) else: - raise ImproperlyConfigured( - '%r is an invalid extension.' % extension) + raise ImproperlyConfigured("%r is an invalid extension." % extension) -class Extension(object): +class Extension: def __init__(self, model, **kwargs): self.model = model for key, value in kwargs.items(): if not hasattr(self, key): - raise TypeError('%s() received an invalid keyword %r' % ( - self.__class__.__name__, key)) + raise TypeError( + "%s() received an invalid keyword %r" + % (self.__class__.__name__, key) + ) setattr(self, key, value) self.handle_model() @@ -92,39 +90,32 @@ def handle_modeladmin(self, modeladmin): pass -def _ensure_list(cls, attribute): - if cls is None: - return - value = getattr(cls, attribute, ()) or () - setattr(cls, attribute, list(value)) - - class ExtensionModelAdmin(admin.ModelAdmin): def __init__(self, *args, **kwargs): - super(ExtensionModelAdmin, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) self.initialize_extensions() def initialize_extensions(self): - if not hasattr(self, '_extensions_initialized'): + if not hasattr(self, "_extensions_initialized"): self._extensions_initialized = True - for extension in getattr(self.model, '_extensions', []): + for extension in getattr(self.model, "_extensions", []): extension.handle_modeladmin(self) def add_extension_options(self, *f): if self.fieldsets is None: return - if isinstance(f[-1], dict): # called with a fieldset + if isinstance(f[-1], dict): # called with a fieldset self.fieldsets.insert(self.fieldset_insertion_index, f) - f[1]['classes'] = list(f[1].get('classes', [])) - f[1]['classes'].append('collapse') - elif f: # assume called with "other" fields + f[1]["classes"] = list(f[1].get("classes", [])) + f[1]["classes"].append("collapse") + elif f: # assume called with "other" fields try: - self.fieldsets[1][1]['fields'].extend(f) + self.fieldsets[1][1]["fields"].extend(f) except IndexError: # Fall back to first fieldset if second does not exist # XXX This is really messy. - self.fieldsets[0][1]['fields'].extend(f) + self.fieldsets[0][1]["fields"].extend(f) def extend_list(self, attribute, iterable): extended = list(getattr(self, attribute, ())) @@ -136,14 +127,14 @@ def prefetch_modeladmin_get_queryset(modeladmin, *lookups): """ Wraps default modeladmin ``get_queryset`` to prefetch related lookups. """ + def do_wrap(f): @wraps(f) def wrapper(request, *args, **kwargs): qs = f(request, *args, **kwargs) qs = qs.prefetch_related(*lookups) return qs + return wrapper - # queryset is renamed to get_queryset in Django 1.6 - fn = "get_queryset" if hasattr(modeladmin, "get_queryset") else "queryset" - setattr(modeladmin, fn, do_wrap(getattr(modeladmin, fn))) + modeladmin.get_queryset = do_wrap(modeladmin.get_queryset) diff --git a/feincms/extensions/changedate.py b/feincms/extensions/changedate.py new file mode 100644 index 000000000..06370bcd9 --- /dev/null +++ b/feincms/extensions/changedate.py @@ -0,0 +1,68 @@ +# ------------------------------------------------------------------------ +# ------------------------------------------------------------------------ +""" +Track the modification date for objects. +""" + +from email.utils import mktime_tz, parsedate_tz +from time import mktime + +from django.db import models +from django.db.models.signals import pre_save +from django.utils import timezone +from django.utils.http import http_date +from django.utils.translation import gettext_lazy as _ + +from feincms import extensions + + +# ------------------------------------------------------------------------ +def pre_save_handler(sender, instance, **kwargs): + """ + Intercept attempts to save and insert the current date and time into + creation and modification date fields. + """ + now = timezone.now() + if instance.id is None: + instance.creation_date = now + instance.modification_date = now + + +# ------------------------------------------------------------------------ +def dt_to_utc_timestamp(dt): + return int(mktime(dt.timetuple())) + + +class Extension(extensions.Extension): + def handle_model(self): + self.model.add_to_class( + "creation_date", + models.DateTimeField(_("creation date"), null=True, editable=False), + ) + self.model.add_to_class( + "modification_date", + models.DateTimeField(_("modification date"), null=True, editable=False), + ) + + self.model.last_modified = lambda p: p.modification_date + + pre_save.connect(pre_save_handler, sender=self.model) + + +# ------------------------------------------------------------------------ +def last_modified_response_processor(page, request, response): + # Don't include Last-Modified if we don't want to be cached + if "no-cache" in response.get("Cache-Control", ""): + return + + # If we already have a Last-Modified, take the later one + last_modified = dt_to_utc_timestamp(page.last_modified()) + if response.has_header("Last-Modified"): + last_modified = max( + last_modified, mktime_tz(parsedate_tz(response["Last-Modified"])) + ) + + response["Last-Modified"] = http_date(last_modified) + + +# ------------------------------------------------------------------------ diff --git a/feincms/extensions/ct_tracker.py b/feincms/extensions/ct_tracker.py new file mode 100644 index 000000000..8dd10ee88 --- /dev/null +++ b/feincms/extensions/ct_tracker.py @@ -0,0 +1,166 @@ +# ------------------------------------------------------------------------ +# +# ct_tracker.py +# FeinCMS +# +# Created by Martin J. Laubach on 02.10.09. +# Copyright (c) 2009 Martin J. Laubach. All rights reserved. +# Updated in 2011 by Matthias Kestenholz for the 1.3 release. +# +# ------------------------------------------------------------------------ + +""" +Track the content types for pages. Instead of gathering the content +types present in each page at run time, save the current state at +saving time, thus saving at least one DB query on page delivery. +""" + +from django.contrib.contenttypes.models import ContentType +from django.db.models.signals import class_prepared, post_save, pre_save +from django.utils.translation import gettext_lazy as _ + +from feincms import extensions +from feincms.contrib.fields import JSONField +from feincms.models import ContentProxy + + +INVENTORY_VERSION = 1 +_translation_map_cache = {} + + +# ------------------------------------------------------------------------ +class TrackerContentProxy(ContentProxy): + def _fetch_content_type_counts(self): + """ + If an object with an empty _ct_inventory is encountered, compute all + the content types currently used on that object and save the list in + the object itself. Further requests for that object can then access + that information and find out which content types are used without + resorting to multiple selects on different ct tables. + + It is therefore important that even an "empty" object does not have an + empty _ct_inventory. + """ + + if "counts" not in self._cache: + if ( + self.item._ct_inventory + and self.item._ct_inventory.get("_version_", -1) == INVENTORY_VERSION + ): + try: + self._cache["counts"] = self._from_inventory( + self.item._ct_inventory + ) + except KeyError: + # It's possible that the inventory does not fit together + # with the current models anymore, f.e. because a content + # type has been removed. + pass + + if "counts" not in self._cache: + super()._fetch_content_type_counts() + + self.item._ct_inventory = self._to_inventory(self._cache["counts"]) + + self.item.__class__.objects.filter(id=self.item.id).update( + _ct_inventory=self.item._ct_inventory + ) + + # Run post save handler by hand + if hasattr(self.item, "get_descendants"): + self.item.get_descendants(include_self=False).update( + _ct_inventory=None + ) + return self._cache["counts"] + + def _translation_map(self): + cls = self.item.__class__ + if cls not in _translation_map_cache: + # Prime translation map and cache it in the class. This needs to be + # done late as opposed to at class definition time as not all + # information is ready, especially when we are doing a "syncdb" the + # ContentType table does not yet exist + tmap = {} + model_to_contenttype = ContentType.objects.get_for_models( + *self.item._feincms_content_types + ) + for idx, fct in enumerate(self.item._feincms_content_types): + dct = model_to_contenttype[fct] + + # Rely on non-negative primary keys + tmap[-dct.id] = idx # From-inventory map + tmap[idx] = dct.id # To-inventory map + + _translation_map_cache[cls] = tmap + return _translation_map_cache[cls] + + def _from_inventory(self, inventory): + """ + Transforms the inventory from Django's content types to FeinCMS's + ContentProxy counts format. + """ + + tmap = self._translation_map() + + return { + region: [(pk, tmap[-ct]) for pk, ct in items] + for region, items in inventory.items() + if region != "_version_" + } + + def _to_inventory(self, counts): + map = self._translation_map() + + inventory = { + region: [(pk, map[ct]) for pk, ct in items] + for region, items in counts.items() + } + inventory["_version_"] = INVENTORY_VERSION + return inventory + + +# ------------------------------------------------------------------------ +def class_prepared_handler(sender, **kwargs): + # It might happen under rare circumstances that not all model classes + # are fully loaded and initialized when the translation map is accessed. + # This leads to (lots of) crashes on the server. Better be safe and + # kill the translation map when any class_prepared signal is received. + _translation_map_cache.clear() + + +class_prepared.connect(class_prepared_handler) + + +# ------------------------------------------------------------------------ +def tree_post_save_handler(sender, instance, **kwargs): + """ + Clobber the _ct_inventory attribute of this object and all sub-objects + on save. + """ + # TODO: Does not find everything it should when ContentProxy content + # inheritance has been customized. + instance.get_descendants(include_self=True).update(_ct_inventory=None) + + +# ------------------------------------------------------------------------ +def single_pre_save_handler(sender, instance, **kwargs): + """Clobber the _ct_inventory attribute of this object""" + + instance._ct_inventory = None + + +# ------------------------------------------------------------------------ +class Extension(extensions.Extension): + def handle_model(self): + self.model.add_to_class( + "_ct_inventory", + JSONField(_("content types"), editable=False, blank=True, null=True), + ) + self.model.content_proxy_class = TrackerContentProxy + + pre_save.connect(single_pre_save_handler, sender=self.model) + if hasattr(self.model, "get_descendants"): + post_save.connect(tree_post_save_handler, sender=self.model) + + +# ------------------------------------------------------------------------ diff --git a/feincms/extensions/datepublisher.py b/feincms/extensions/datepublisher.py new file mode 100644 index 000000000..cdf2786a4 --- /dev/null +++ b/feincms/extensions/datepublisher.py @@ -0,0 +1,153 @@ +""" +Allows setting a date range for when the page is active. Modifies the active() +manager method so that only pages inside the given range are used in the +default views and the template tags. + +Depends on the page class having a "active_filters" list that will be used by +the page's manager to determine which entries are to be considered active. +""" +# ------------------------------------------------------------------------ + +from datetime import datetime + +import django +from django.db import models +from django.db.models import Q +from django.utils import timezone +from django.utils.cache import patch_response_headers +from django.utils.html import mark_safe +from django.utils.translation import gettext_lazy as _ + +from feincms import extensions + + +# ------------------------------------------------------------------------ +def format_date(d, if_none=""): + """ + Format a date in a nice human readable way: Omit the year if it's the + current year. Also return a default value if no date is passed in. + """ + + if d is None: + return if_none + + now = timezone.now() + fmt = (d.year == now.year) and "%d.%m" or "%d.%m.%Y" + return d.strftime(fmt) + + +def latest_children(self): + return self.get_children().order_by("-publication_date") + + +# ------------------------------------------------------------------------ +def granular_now(n=None, default_tz=None): + """ + A datetime.now look-alike that returns times rounded to a five minute + boundary. This helps the backend database to optimize/reuse/cache its + queries by not creating a brand new query each time. + + Also useful if you are using johnny-cache or a similar queryset cache. + """ + if n is None: + n = timezone.now() + if default_tz is None: + default_tz = timezone.get_current_timezone() + + rounded_minute = (n.minute // 5) * 5 + d = datetime(n.year, n.month, n.day, n.hour, rounded_minute) + if django.VERSION < (5,): + return timezone.make_aware(d, default_tz, is_dst=True) + else: + return timezone.make_aware(d, default_tz) + + +# ------------------------------------------------------------------------ +def datepublisher_response_processor(page, request, response): + """ + This response processor is automatically added when the datepublisher + extension is registered. It sets the response headers to match with + the publication end date of the page so that upstream caches and + the django caching middleware know when to expunge the copy. + """ + expires = page.publication_end_date + if expires is not None: + delta = expires - timezone.now() + delta = int(delta.days * 86400 + delta.seconds) + patch_response_headers(response, delta) + + +# ------------------------------------------------------------------------ +class Extension(extensions.Extension): + def handle_model(self): + self.model.add_to_class( + "publication_date", + models.DateTimeField(_("publication date"), default=granular_now), + ) + self.model.add_to_class( + "publication_end_date", + models.DateTimeField( + _("publication end date"), + blank=True, + null=True, + help_text=_("Leave empty if the entry should stay active forever."), + ), + ) + self.model.add_to_class("latest_children", latest_children) + + # Patch in rounding the pub and pub_end dates on save + orig_save = self.model.save + + def granular_save(obj, *args, **kwargs): + if obj.publication_date: + obj.publication_date = granular_now(obj.publication_date) + if obj.publication_end_date: + obj.publication_end_date = granular_now(obj.publication_end_date) + orig_save(obj, *args, **kwargs) + + self.model.save = granular_save + + # Append publication date active check + if hasattr(self.model._default_manager, "add_to_active_filters"): + self.model._default_manager.add_to_active_filters( + lambda queryset: queryset.filter( + Q(publication_date__lte=granular_now()) + & ( + Q(publication_end_date__isnull=True) + | Q(publication_end_date__gt=granular_now()) + ) + ), + key="datepublisher", + ) + + # Processor to patch up response headers for expiry date + self.model.register_response_processor(datepublisher_response_processor) + + def handle_modeladmin(self, modeladmin): + def datepublisher_admin(self, obj): + return mark_safe( + "%s – %s" + % ( + format_date(obj.publication_date), + format_date(obj.publication_end_date, "∞"), + ) + ) + + datepublisher_admin.short_description = _("visible from - to") + + modeladmin.__class__.datepublisher_admin = datepublisher_admin + + try: + pos = modeladmin.list_display.index("is_visible_admin") + except ValueError: + pos = len(modeladmin.list_display) + + modeladmin.list_display.insert(pos + 1, "datepublisher_admin") + + modeladmin.add_extension_options( + _("Date-based publishing"), + {"fields": ["publication_date", "publication_end_date"]}, + ) + + +# ------------------------------------------------------------------------ diff --git a/feincms/extensions/featured.py b/feincms/extensions/featured.py new file mode 100644 index 000000000..a15535571 --- /dev/null +++ b/feincms/extensions/featured.py @@ -0,0 +1,20 @@ +""" +Add a "featured" field to objects so admins can better direct top content. +""" + +from django.db import models +from django.utils.translation import gettext_lazy as _ + +from feincms import extensions + + +class Extension(extensions.Extension): + def handle_model(self): + self.model.add_to_class( + "featured", models.BooleanField(_("featured"), default=False) + ) + + def handle_modeladmin(self, modeladmin): + modeladmin.add_extension_options( + _("Featured"), {"fields": ("featured",), "classes": ("collapse",)} + ) diff --git a/feincms/extensions/seo.py b/feincms/extensions/seo.py new file mode 100644 index 000000000..3698c00d5 --- /dev/null +++ b/feincms/extensions/seo.py @@ -0,0 +1,40 @@ +""" +Add a keyword and a description field which are helpful for SEO optimization. +""" + +from django.db import models +from django.utils.translation import gettext_lazy as _ + +from feincms import extensions + + +class Extension(extensions.Extension): + def handle_model(self): + self.model.add_to_class( + "meta_keywords", + models.TextField( + _("meta keywords"), + blank=True, + help_text=_("Keywords are ignored by most search engines."), + ), + ) + self.model.add_to_class( + "meta_description", + models.TextField( + _("meta description"), + blank=True, + help_text=_( + "This text is displayed on the search results page. " + "It is however not used for the SEO ranking. " + "Text longer than 140 characters is truncated." + ), + ), + ) + + def handle_modeladmin(self, modeladmin): + modeladmin.extend_list("search_fields", ["meta_keywords", "meta_description"]) + + modeladmin.add_extension_options( + _("Search engine optimization"), + {"fields": ("meta_keywords", "meta_description"), "classes": ("collapse",)}, + ) diff --git a/feincms/extensions/translations.py b/feincms/extensions/translations.py new file mode 100644 index 000000000..7465b1072 --- /dev/null +++ b/feincms/extensions/translations.py @@ -0,0 +1,321 @@ +# ------------------------------------------------------------------------ +# ------------------------------------------------------------------------ + +""" +This extension adds a language field to every page. When calling the request +processors the page's language is activated. +Pages in secondary languages can be said to be a translation of a page in the +primary language (the first language in settings.LANGUAGES), thereby enabling +deeplinks between translated pages. + +It is recommended to activate +:class:`django.middleware.locale.LocaleMiddleware` so that the correct language +will be activated per user or session even for non-FeinCMS managed views such +as Django's administration tool. +""" + +# ------------------------------------------------------------------------ +import logging +from typing import Optional + +from django.conf import settings as django_settings +from django.db import models +from django.http import HttpRequest, HttpResponseRedirect +from django.utils import translation +from django.utils.safestring import mark_safe +from django.utils.translation import gettext_lazy as _ + +from feincms import extensions, settings +from feincms._internal import monkeypatch_method, monkeypatch_property +from feincms.translations import is_primary_language + + +# ------------------------------------------------------------------------ +logger = logging.getLogger(__name__) + +LANGUAGE_COOKIE_NAME: str = django_settings.LANGUAGE_COOKIE_NAME +if hasattr(translation, "LANGUAGE_SESSION_KEY"): + LANGUAGE_SESSION_KEY = translation.LANGUAGE_SESSION_KEY +else: + # stopgap measure for django >= 4, need to revisit this later + LANGUAGE_SESSION_KEY = LANGUAGE_COOKIE_NAME + +PRIMARY_LANGUAGE: str = django_settings.LANGUAGES[0][0] + + +# ------------------------------------------------------------------------ +def user_has_language_set(request: HttpRequest) -> bool: + """ + Determine whether the user has explicitely set a language earlier on. + This is taken later on as an indication that we should not mess with the + site's language settings, after all, the user's decision is what counts. + """ + if ( + hasattr(request, "session") + and request.session.get(LANGUAGE_SESSION_KEY) is not None + ): + return True + if LANGUAGE_COOKIE_NAME in request.COOKIES: + return True + return False + + +# ------------------------------------------------------------------------ +def translation_allowed_language(select_language: str) -> str: + "Check for feincms specific set of allowed front end languages." + if settings.FEINCMS_FRONTEND_LANGUAGES: + language = select_language[:2] + if language not in settings.FEINCMS_FRONTEND_LANGUAGES: + select_language = PRIMARY_LANGUAGE + + return select_language + + +# ------------------------------------------------------------------------ +def translation_set_language( + request: HttpRequest, select_language: str +) -> Optional[HttpResponseRedirect]: + """ + Set and activate a language, if that language is available. + """ + + select_language = translation_allowed_language(select_language) + + if translation.check_for_language(select_language): + fallback = False + else: + # The page is in a language that Django has no messages for. + # We display anyhow, but fall back to primary language for + # other messages and other applications. It is *highly* recommended to + # create a new django.po for the language instead of + # using this behaviour. + select_language = PRIMARY_LANGUAGE + fallback = True + + translation.activate(select_language) + request.LANGUAGE_CODE = translation.get_language() # type: ignore + + if hasattr(request, "session"): + # User has a session, then set this language there + current_session_language = request.session.get( + LANGUAGE_SESSION_KEY, PRIMARY_LANGUAGE + ) + + if select_language != current_session_language: + request.session[LANGUAGE_SESSION_KEY] = select_language + elif request.method == "GET" and not fallback: + # No session is active. We need to set a cookie for the language + # so that it persists when users change their location to somewhere + # not under the control of the CMS. + # Only do this when request method is GET (mainly, do not abort + # POST requests) + response = HttpResponseRedirect(request.get_full_path()) + response.set_cookie(str(LANGUAGE_COOKIE_NAME), select_language, samesite="Lax") + return response + + +# ------------------------------------------------------------------------ +def translations_request_processor_explicit(page, request): + # If this page is just a redirect, don't do any language specific setup + if page.redirect_to: + return + + # Until further notice, the user might be wanting to switch to the + # page's language... + desired_language = page.language + + # ...except if the user explicitely wants to switch language + if "set_language" in request.GET: + desired_language = request.GET["set_language"] + # ...or the user already has explicitely set a language, bail out and + # don't change it for them behind their back + elif user_has_language_set(request): + return + + return translation_set_language(request, desired_language) + + +# ------------------------------------------------------------------------ +def translations_request_processor_standard(page, request): + # If this page is just a redirect, don't do any language specific setup + if getattr(page, "redirect_to", None): + return + + if page.language == translation.get_language(): + return + + return translation_set_language(request, page.language) + + +# ------------------------------------------------------------------------ +def get_current_language_code(request): + language_code = getattr(request, "LANGUAGE_CODE", None) + if language_code is None: + logger.warning( + "Could not access request.LANGUAGE_CODE. Is 'django.middleware." + "locale.LocaleMiddleware' in MIDDLEWARE_CLASSES?" + ) + return language_code + + +# ------------------------------------------------------------------------ +class Extension(extensions.Extension): + def handle_model(self): + cls = self.model + + cls.add_to_class( + "language", + models.CharField( + _("language"), + max_length=10, + choices=django_settings.LANGUAGES, + default=PRIMARY_LANGUAGE, + ), + ) + cls.add_to_class( + "translation_of", + models.ForeignKey( + "self", + on_delete=models.CASCADE, + blank=True, + null=True, + verbose_name=_("translation of"), + related_name="translations", + limit_choices_to={"language": PRIMARY_LANGUAGE}, + help_text=_("Leave this empty for entries in the primary language."), + ), + ) + + if hasattr(cls, "register_request_processor"): + if settings.FEINCMS_TRANSLATION_POLICY == "EXPLICIT": + cls.register_request_processor( + translations_request_processor_explicit, key="translations" + ) + else: # STANDARD + cls.register_request_processor( + translations_request_processor_standard, key="translations" + ) + + if hasattr(cls, "get_redirect_to_target"): + original_get_redirect_to_target = cls.get_redirect_to_target + + @monkeypatch_method(cls) + def get_redirect_to_target(self, request=None): + """ + Find an acceptable redirect target. If this is a local link, + then try to find the page this redirect references and + translate it according to the user's language. This way, one + can easily implement a localized "/"-url to welcome page + redirection. + """ + target = original_get_redirect_to_target(self, request) + if target and target.find("//") == -1: + # Not an offsite link http://bla/blubb + try: + page = cls.objects.page_for_path(target) + language = get_current_language_code(request) + language = translation_allowed_language(language) + page = page.get_translation(language) + # Note: Does not care about active status? + target = page.get_absolute_url() + except cls.DoesNotExist: + pass + return target + + @monkeypatch_method(cls) + def available_translations(self): + if not self.id: # New, unsaved pages have no translations + return [] + + if hasattr(cls.objects, "apply_active_filters"): + filter_active = cls.objects.apply_active_filters + else: + + def filter_active(queryset): + return queryset + + if is_primary_language(self.language): + return filter_active(self.translations.all()) + elif self.translation_of: + # reuse prefetched queryset, do not filter it + res = [ + t + for t in filter_active(self.translation_of.translations.all()) + if t.language != self.language + ] + res.insert(0, self.translation_of) + return res + else: + return [] + + @monkeypatch_method(cls) + def get_original_translation(self, *args, **kwargs): + if is_primary_language(self.language): + return self + if self.translation_of: + return self.translation_of + logger.debug( + "Page pk=%d (%s) has no primary language translation (%s)", + self.pk, + self.language, + PRIMARY_LANGUAGE, + ) + return self + + @monkeypatch_property(cls) + def original_translation(self): + return self.get_original_translation() + + @monkeypatch_method(cls) + def get_translation(self, language): + return self.original_translation.translations.get(language=language) + + def handle_modeladmin(self, modeladmin): + extensions.prefetch_modeladmin_get_queryset( + modeladmin, "translation_of__translations", "translations" + ) + + def available_translations_admin(self, page): + # Do not use available_translations() because we don't care + # whether pages are active or not here. + translations = [page] + translations.extend(page.translations.all()) + if page.translation_of: + translations.append(page.translation_of) + translations.extend(page.translation_of.translations.all()) + translations = {p.language: p.id for p in translations} + + links = [] + + for key, title in django_settings.LANGUAGES: + if key == page.language: + continue + + if key in translations: + links.append( + '%s' + % (translations[key], _("Edit translation"), key.upper()) + ) + else: + links.append( + '%s' + % (page.id, key, _("Create translation"), key.upper()) + ) + + return mark_safe(" | ".join(links)) + + available_translations_admin.short_description = _("translations") + modeladmin.__class__.available_translations_admin = available_translations_admin + + if hasattr(modeladmin, "add_extension_options"): + modeladmin.add_extension_options("language", "translation_of") + + modeladmin.extend_list( + "list_display", ["language", "available_translations_admin"] + ) + modeladmin.extend_list("list_filter", ["language"]) + modeladmin.extend_list("raw_id_fields", ["translation_of"]) + + +# ------------------------------------------------------------------------ diff --git a/feincms/locale/ca/LC_MESSAGES/django.po b/feincms/locale/ca/LC_MESSAGES/django.po index 645d74bc3..d415b080b 100644 --- a/feincms/locale/ca/LC_MESSAGES/django.po +++ b/feincms/locale/ca/LC_MESSAGES/django.po @@ -1,744 +1,775 @@ # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. -# +# # Translators: # proycon , 2009 msgid "" msgstr "" "Project-Id-Version: FeinCMS\n" -"Report-Msgid-Bugs-To: http://github.com/feincms/feincms/issues\n" -"POT-Creation-Date: 2013-10-25 11:12+0200\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2014-07-20 14:59+0200\n" "PO-Revision-Date: 2013-10-25 09:15+0000\n" "Last-Translator: Matthias Kestenholz \n" -"Language-Team: Catalan (http://www.transifex.com/projects/p/feincms/language/ca/)\n" +"Language-Team: Catalan (http://www.transifex.com/projects/p/feincms/language/" +"ca/)\n" +"Language: ca\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Language: ca\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: models.py:370 content/template/models.py:59 -msgid "template" -msgstr "plantilla" - -#: models.py:510 -msgid "ordering" -msgstr "ordenació" - -#: translations.py:259 module/blog/extensions/translations.py:17 -#: module/extensions/translations.py:123 -msgid "language" -msgstr "idioma" - -#: admin/filterspecs.py:35 admin/filterspecs.py:70 +#: admin/filterspecs.py:47 admin/filterspecs.py:86 msgid "All" msgstr "Tots" -#: admin/filterspecs.py:46 module/page/models.py:172 +#: admin/filterspecs.py:58 module/page/models.py:178 msgid "Parent" msgstr "Pare" -#: admin/filterspecs.py:81 +#: admin/filterspecs.py:97 #: templates/admin/medialibrary/mediafile/change_list.html:14 msgid "Category" msgstr "Categoria" -#: admin/item_editor.py:159 +#: admin/item_editor.py:190 #, python-format msgid "Change %s" msgstr "Modificar %s" -#: admin/tree_editor.py:234 content/rss/models.py:20 -#: content/section/models.py:34 module/blog/models.py:29 -#: module/medialibrary/models.py:40 module/page/models.py:170 -#: module/page/models.py:226 +#: admin/tree_editor.py:294 content/rss/models.py:23 +#: content/section/models.py:35 module/blog/models.py:35 +#: module/medialibrary/models.py:45 module/page/models.py:172 +#: module/page/models.py:240 msgid "title" msgstr "títol" -#: admin/tree_editor.py:407 +#: admin/tree_editor.py:353 admin/tree_editor.py:370 +msgid "You do not have permission to modify this object" +msgstr "" + +#: admin/tree_editor.py:491 +msgid "No permission" +msgstr "" + +#: admin/tree_editor.py:509 #, python-format msgid "%s has been moved to a new position." msgstr "%s ha sigut mogut a una nova posició" -#: admin/tree_editor.py:411 +#: admin/tree_editor.py:512 msgid "Did not understand moving instruction." msgstr "No entenc l'odre de moviment" -#: admin/tree_editor.py:420 +#: admin/tree_editor.py:523 msgid "actions" msgstr "accions" -#: admin/tree_editor.py:432 +#: admin/tree_editor.py:552 #, python-format -msgid "Successfully deleted %s items." +msgid "Successfully deleted %(count)d items." msgstr "" -#: admin/tree_editor.py:440 +#: admin/tree_editor.py:565 #, python-format msgid "Delete selected %(verbose_name_plural)s" msgstr "" -#: content/application/models.py:218 +#: content/application/models.py:147 msgid "application content" msgstr "contingut de l'aplicació" -#: content/application/models.py:219 +#: content/application/models.py:148 msgid "application contents" msgstr "continguts de l'aplicació" -#: content/application/models.py:249 +#: content/application/models.py:179 msgid "application" msgstr "aplicació" -#: content/comments/models.py:24 +#: content/comments/models.py:32 msgid "enabled" msgstr "actiu" -#: content/comments/models.py:24 +#: content/comments/models.py:33 msgid "New comments may be added" msgstr "Es podran afegir nous comentaris" -#: content/comments/models.py:28 content/comments/models.py:29 +#: content/comments/models.py:37 content/comments/models.py:38 msgid "comments" msgstr "comentaris" -#: content/comments/models.py:49 +#: content/comments/models.py:60 msgid "public" msgstr "públic" -#: content/comments/models.py:49 +#: content/comments/models.py:61 msgid "not public" msgstr "privat" -#: content/contactform/models.py:18 +#: content/contactform/models.py:20 msgid "name" msgstr "nom" -#: content/contactform/models.py:19 +#: content/contactform/models.py:21 msgid "email" msgstr "correu" -#: content/contactform/models.py:20 +#: content/contactform/models.py:22 msgid "subject" msgstr "assumpte" -#: content/contactform/models.py:23 content/raw/models.py:14 +#: content/contactform/models.py:26 content/raw/models.py:16 msgid "content" msgstr "contingut" -#: content/contactform/models.py:34 +#: content/contactform/models.py:37 msgid "contact form" msgstr "formulari de contacte" -#: content/contactform/models.py:35 +#: content/contactform/models.py:38 msgid "contact forms" msgstr "formularis de contacte" -#: content/file/models.py:20 content/file/models.py:25 -#: module/medialibrary/models.py:84 +#: content/file/models.py:23 content/file/models.py:28 +#: module/medialibrary/models.py:94 msgid "file" msgstr "arxiu" -#: content/file/models.py:26 +#: content/file/models.py:29 msgid "files" msgstr "arxius" -#: content/image/models.py:45 content/image/models.py:54 +#: content/image/models.py:46 content/image/models.py:55 msgid "image" msgstr "imatge" -#: content/image/models.py:48 +#: content/image/models.py:49 msgid "alternate text" msgstr "" -#: content/image/models.py:49 +#: content/image/models.py:50 msgid "Description of image" msgstr "" -#: content/image/models.py:50 module/medialibrary/models.py:231 +#: content/image/models.py:51 module/medialibrary/models.py:260 msgid "caption" msgstr "llegenda" -#: content/image/models.py:55 +#: content/image/models.py:56 msgid "images" msgstr "imatges" -#: content/image/models.py:80 +#: content/image/models.py:82 msgid "position" msgstr "posició" -#: content/image/models.py:88 +#: content/image/models.py:90 msgid "format" msgstr "" -#: content/medialibrary/models.py:49 content/section/models.py:36 -#: module/medialibrary/fields.py:59 module/medialibrary/models.py:97 +#: content/medialibrary/models.py:51 content/section/models.py:38 +#: module/medialibrary/fields.py:66 module/medialibrary/models.py:112 msgid "media file" msgstr "arxiu de medis" -#: content/medialibrary/models.py:50 module/medialibrary/models.py:98 +#: content/medialibrary/models.py:52 module/medialibrary/models.py:113 msgid "media files" msgstr "arxius de medis" -#: content/medialibrary/models.py:58 content/section/models.py:52 -#: content/table/models.py:82 +#: content/medialibrary/models.py:64 content/section/models.py:59 msgid "type" msgstr "tipus" -#: content/raw/models.py:18 +#: content/raw/models.py:20 msgid "raw content" msgstr "contingut en cruu" -#: content/raw/models.py:19 +#: content/raw/models.py:21 msgid "raw contents" msgstr "continguts en cruu" -#: content/richtext/models.py:22 +#: content/richtext/models.py:24 msgid "HTML Tidy" msgstr "HTML Tidy" -#: content/richtext/models.py:23 +#: content/richtext/models.py:25 msgid "Ignore the HTML validation warnings" msgstr "Ignora els avisos de validació de l'HTML" -#: content/richtext/models.py:47 +#: content/richtext/models.py:55 #, python-format msgid "" "HTML validation produced %(count)d warnings. Please review the updated " "content below before continuing: %(messages)s" -msgstr "La validació de l'HTML generà %(count)d avisos. Per favor, revisa el contingut actualitzat de la part inferior abans de continuar: %(messages)s" +msgstr "" +"La validació de l'HTML generà %(count)d avisos. Per favor, revisa el " +"contingut actualitzat de la part inferior abans de continuar: %(messages)s" -#: content/richtext/models.py:84 content/section/models.py:35 +#: content/richtext/models.py:99 content/section/models.py:36 msgid "text" msgstr "text" -#: content/richtext/models.py:88 +#: content/richtext/models.py:103 msgid "rich text" msgstr "text ric" -#: content/richtext/models.py:89 +#: content/richtext/models.py:104 msgid "rich texts" msgstr "texts rics" -#: content/rss/models.py:21 +#: content/rss/models.py:25 msgid "" "The rss field is updated several times a day. A change in the title will " "only be visible on the home page after the next feed update." -msgstr "El feed RSS s'actualitza varies vegades al dia. Els canvis en el títol sols seran visibles a la plana inicial després de la propera actualització." +msgstr "" +"El feed RSS s'actualitza varies vegades al dia. Els canvis en el títol sols " +"seran visibles a la plana inicial després de la propera actualització." -#: content/rss/models.py:22 +#: content/rss/models.py:28 msgid "link" msgstr "enlaç" -#: content/rss/models.py:23 +#: content/rss/models.py:30 msgid "pre-rendered content" msgstr "contingut pre-renderitzat" -#: content/rss/models.py:24 +#: content/rss/models.py:32 msgid "last updated" msgstr "darrera actualització" -#: content/rss/models.py:25 +#: content/rss/models.py:33 msgid "max. items" msgstr "nombre màxim" -#: content/rss/models.py:29 +#: content/rss/models.py:37 msgid "RSS feed" msgstr "feed RSS" -#: content/rss/models.py:30 +#: content/rss/models.py:38 msgid "RSS feeds" msgstr "feeds RSS" -#: content/section/models.py:41 +#: content/section/models.py:43 msgid "section" msgstr "secció" -#: content/section/models.py:42 +#: content/section/models.py:44 msgid "sections" msgstr "seccions" -#: content/table/models.py:63 -msgid "plain" -msgstr "plà" - -#: content/table/models.py:64 -msgid "title row" -msgstr "títol de la fila" - -#: content/table/models.py:66 -msgid "title row and column" -msgstr "títol de fila i columna" - -#: content/table/models.py:72 -msgid "table" -msgstr "taula" - -#: content/table/models.py:73 -msgid "tables" -msgstr "taules" - -#: content/table/models.py:87 -msgid "data" -msgstr "dades" - -#: content/template/models.py:51 +#: content/template/models.py:53 msgid "template content" msgstr "plantilla de contingut" -#: content/template/models.py:52 +#: content/template/models.py:54 msgid "template contents" msgstr "plantilles de continguts" -#: content/video/models.py:31 +#: content/template/models.py:63 models.py:400 +msgid "template" +msgstr "plantilla" + +#: content/video/models.py:34 msgid "video link" msgstr "enllaç de vídeo" -#: content/video/models.py:32 +#: content/video/models.py:36 msgid "" -"This should be a link to a youtube or vimeo video, i.e.: " -"http://www.youtube.com/watch?v=zmj1rpzDRZ0" -msgstr "Això ha de ser un enllaç a un vídeo de Youtube o Vimeo. Per exemple: http://www.youtube.com/watch?v=zmj1rpzDRZ0" +"This should be a link to a youtube or vimeo video, i.e.: http://www.youtube." +"com/watch?v=zmj1rpzDRZ0" +msgstr "" +"Això ha de ser un enllaç a un vídeo de Youtube o Vimeo. Per exemple: http://" +"www.youtube.com/watch?v=zmj1rpzDRZ0" -#: content/video/models.py:37 +#: content/video/models.py:41 msgid "video" msgstr "vídeo" -#: content/video/models.py:38 +#: content/video/models.py:42 msgid "videos" msgstr "vídeos" -#: contrib/tagging.py:117 +#: contrib/tagging.py:132 msgid "Tagging" msgstr "" -#: module/blog/models.py:28 +#: models.py:550 +msgid "ordering" +msgstr "ordenació" + +#: module/blog/extensions/tags.py:17 +msgid "tags" +msgstr "etiquetes" + +#: module/blog/extensions/translations.py:25 +#: module/extensions/translations.py:140 translations.py:282 +msgid "language" +msgstr "idioma" + +#: module/blog/extensions/translations.py:35 +#: module/extensions/translations.py:148 +msgid "translation of" +msgstr "tradució de" + +#: module/blog/extensions/translations.py:39 +#: module/extensions/translations.py:152 +msgid "Leave this empty for entries in the primary language." +msgstr "Deixa aquest camp buid per les entrades en l'idioma base" + +#: module/blog/extensions/translations.py:67 +#: templates/admin/feincms/item_editor.html:33 +msgid "available translations" +msgstr "traduccions disponibles" + +#: module/blog/models.py:33 msgid "published" msgstr "publicat" -#: module/blog/models.py:30 +#: module/blog/models.py:36 msgid "This is used for the generated navigation too." msgstr "Això també es fa servir per la navegació generada automàticament." -#: module/blog/models.py:33 +#: module/blog/models.py:40 msgid "published on" msgstr "publicat el" -#: module/blog/models.py:34 -msgid "" -"Will be set automatically once you tick the `published` checkbox above." -msgstr "S'establirà automàticament quan marquis la casella superior de 'publicat'" +#: module/blog/models.py:42 +msgid "Will be set automatically once you tick the `published` checkbox above." +msgstr "" +"S'establirà automàticament quan marquis la casella superior de 'publicat'" -#: module/blog/models.py:39 +#: module/blog/models.py:48 msgid "entry" msgstr "entrada" -#: module/blog/models.py:40 +#: module/blog/models.py:49 msgid "entries" msgstr "entrades" -#: module/blog/extensions/tags.py:12 -msgid "tags" -msgstr "etiquetes" - -#: module/blog/extensions/translations.py:20 -#: module/extensions/translations.py:126 -msgid "translation of" -msgstr "tradució de" - -#: module/blog/extensions/translations.py:23 -#: module/extensions/translations.py:129 -msgid "Leave this empty for entries in the primary language." -msgstr "Deixa aquest camp buid per les entrades en l'idioma base" - -#: module/blog/extensions/translations.py:44 -#: templates/admin/feincms/item_editor.html:43 -msgid "available translations" -msgstr "traduccions disponibles" - -#: module/extensions/changedate.py:32 +#: module/extensions/changedate.py:41 msgid "creation date" msgstr "data de creació" -#: module/extensions/changedate.py:33 +#: module/extensions/changedate.py:43 msgid "modification date" msgstr "data de modificació" -#: module/extensions/ct_tracker.py:140 +#: module/extensions/ct_tracker.py:156 msgid "content types" msgstr "tipus de contingut" -#: module/extensions/datepublisher.py:67 +#: module/extensions/datepublisher.py:86 msgid "publication date" msgstr "data de publicació" -#: module/extensions/datepublisher.py:70 +#: module/extensions/datepublisher.py:90 msgid "publication end date" msgstr "publicar fins a" -#: module/extensions/datepublisher.py:72 +#: module/extensions/datepublisher.py:93 msgid "Leave empty if the entry should stay active forever." msgstr "Si la deixen en blanc l'entrada estarà activa per sempre" -#: module/extensions/datepublisher.py:103 +#: module/extensions/datepublisher.py:128 msgid "visible from - to" msgstr "visible de-fins a" -#: module/extensions/datepublisher.py:113 +#: module/extensions/datepublisher.py:139 msgid "Date-based publishing" msgstr "Publicació segons la data" -#: module/extensions/featured.py:9 +#: module/extensions/featured.py:15 msgid "featured" msgstr "categoritzat" -#: module/extensions/featured.py:14 +#: module/extensions/featured.py:21 msgid "Featured" msgstr "Categoritzat" -#: module/extensions/seo.py:9 +#: module/extensions/seo.py:16 msgid "meta keywords" msgstr "meta paraules" -#: module/extensions/seo.py:10 -msgid "This will be prepended to the default keyword list." -msgstr "Això serà afegit a la llista de paraules clau per defecte" +#: module/extensions/seo.py:18 +msgid "Keywords are ignored by most search engines." +msgstr "" -#: module/extensions/seo.py:11 +#: module/extensions/seo.py:20 msgid "meta description" msgstr "meta descripció" -#: module/extensions/seo.py:12 -msgid "This will be prepended to the default description." -msgstr "Això serà afegit a la descripció per defecte" +#: module/extensions/seo.py:22 +msgid "" +"This text is displayed on the search results page. It is however not used " +"for the SEO ranking. Text longer than 140 characters is truncated." +msgstr "" -#: module/extensions/seo.py:17 +#: module/extensions/seo.py:32 msgid "Search engine optimization" msgstr "Optimització per als motors de cerca" -#: module/extensions/translations.py:207 +#: module/extensions/translations.py:249 msgid "Edit translation" msgstr "Editar la traducció" -#: module/extensions/translations.py:210 +#: module/extensions/translations.py:256 msgid "Create translation" msgstr "Crear traducció" -#: module/extensions/translations.py:215 +#: module/extensions/translations.py:264 msgid "translations" msgstr "traduccions" -#: module/medialibrary/forms.py:26 +#: module/medialibrary/forms.py:31 msgid "This would create a loop in the hierarchy" msgstr "" -#: module/medialibrary/forms.py:64 +#: module/medialibrary/forms.py:77 #, python-format msgid "" "Cannot overwrite with different file type (attempt to overwrite a " "%(old_ext)s with a %(new_ext)s)" msgstr "" -#: module/medialibrary/modeladmins.py:58 +#: module/medialibrary/modeladmins.py:63 #, python-format msgid "Successfully added %(count)d media file to %(category)s." msgid_plural "Successfully added %(count)d media files to %(category)s." msgstr[0] "" msgstr[1] "" -#: module/medialibrary/modeladmins.py:77 +#: module/medialibrary/modeladmins.py:84 msgid "Add selected media files to category" msgstr "" -#: module/medialibrary/modeladmins.py:86 +#: module/medialibrary/modeladmins.py:94 #, python-format msgid "ZIP file exported as %s" msgstr "" -#: module/medialibrary/modeladmins.py:88 +#: module/medialibrary/modeladmins.py:96 #, python-format msgid "ZIP file export failed: %s" msgstr "" -#: module/medialibrary/modeladmins.py:93 +#: module/medialibrary/modeladmins.py:104 msgid "Export selected media files as zip file" msgstr "" -#: module/medialibrary/modeladmins.py:136 +#: module/medialibrary/modeladmins.py:157 #: templates/admin/feincms/page/page/item_editor.html:15 msgid "Preview" msgstr "Previsualitzar" -#: module/medialibrary/modeladmins.py:141 module/medialibrary/models.py:88 +#: module/medialibrary/modeladmins.py:162 module/medialibrary/models.py:103 msgid "file size" msgstr "tamany d'arxiu" -#: module/medialibrary/modeladmins.py:146 module/medialibrary/models.py:86 +#: module/medialibrary/modeladmins.py:167 module/medialibrary/models.py:100 msgid "created" msgstr "creat" -#: module/medialibrary/modeladmins.py:165 module/medialibrary/models.py:85 +#: module/medialibrary/modeladmins.py:187 module/medialibrary/models.py:97 msgid "file type" msgstr "tipus d'arxiu" -#: module/medialibrary/modeladmins.py:186 +#: module/medialibrary/modeladmins.py:211 msgid "file info" msgstr "informació de l'arxiu" -#: module/medialibrary/modeladmins.py:198 +#: module/medialibrary/modeladmins.py:224 #, python-format msgid "%d files imported" msgstr "" -#: module/medialibrary/modeladmins.py:200 +#: module/medialibrary/modeladmins.py:226 #, python-format msgid "ZIP import failed: %s" msgstr "" -#: module/medialibrary/modeladmins.py:202 +#: module/medialibrary/modeladmins.py:228 msgid "No input file given" msgstr "" -#: module/medialibrary/models.py:43 +#: module/medialibrary/models.py:49 msgid "parent" msgstr "pare" -#: module/medialibrary/models.py:45 module/page/models.py:171 +#: module/medialibrary/models.py:51 module/page/models.py:175 msgid "slug" msgstr "slug" -#: module/medialibrary/models.py:49 +#: module/medialibrary/models.py:55 msgid "category" msgstr "categoria" -#: module/medialibrary/models.py:50 module/medialibrary/models.py:90 +#: module/medialibrary/models.py:56 module/medialibrary/models.py:106 msgid "categories" msgstr "categories" -#: module/medialibrary/models.py:87 +#: module/medialibrary/models.py:101 msgid "copyright" msgstr "drets de còpia" -#: module/medialibrary/models.py:202 +#: module/medialibrary/models.py:219 msgid "Image" msgstr "Imatge" -#: module/medialibrary/models.py:203 +#: module/medialibrary/models.py:221 msgid "Video" msgstr "Vídeo" -#: module/medialibrary/models.py:204 +#: module/medialibrary/models.py:224 msgid "Audio" msgstr "Audio" -#: module/medialibrary/models.py:205 +#: module/medialibrary/models.py:226 msgid "PDF document" msgstr "document PDF" -#: module/medialibrary/models.py:206 +#: module/medialibrary/models.py:227 msgid "Flash" msgstr "Flash" -#: module/medialibrary/models.py:207 +#: module/medialibrary/models.py:228 msgid "Text" msgstr "Text" -#: module/medialibrary/models.py:208 +#: module/medialibrary/models.py:229 msgid "Rich Text" msgstr "Text ric" -#: module/medialibrary/models.py:209 +#: module/medialibrary/models.py:230 msgid "Zip archive" msgstr "" -#: module/medialibrary/models.py:210 +#: module/medialibrary/models.py:231 msgid "Microsoft Word" msgstr "Microsoft Word" -#: module/medialibrary/models.py:211 +#: module/medialibrary/models.py:233 msgid "Microsoft Excel" msgstr "Microsoft Excel" -#: module/medialibrary/models.py:212 +#: module/medialibrary/models.py:235 msgid "Microsoft PowerPoint" msgstr "Microsoft PowerPoint" -#: module/medialibrary/models.py:213 +#: module/medialibrary/models.py:237 msgid "Binary" msgstr "Binari" -#: module/medialibrary/models.py:232 +#: module/medialibrary/models.py:261 msgid "description" msgstr "descripció" -#: module/medialibrary/models.py:235 +#: module/medialibrary/models.py:264 msgid "media file translation" msgstr "traducció de l'arxiu de medis" -#: module/medialibrary/models.py:236 +#: module/medialibrary/models.py:265 msgid "media file translations" msgstr "traduccions dels arxius de medis" -#: module/page/forms.py:172 +#: module/page/extensions/excerpt.py:18 +msgid "excerpt" +msgstr "extracte" + +#: module/page/extensions/excerpt.py:21 +msgid "Add a brief excerpt summarizing the content of this page." +msgstr "Afegeix una breu descripció resumint el contingut de la plana" + +#: module/page/extensions/excerpt.py:25 +msgid "Excerpt" +msgstr "Extracte" + +#: module/page/extensions/navigation.py:86 +#: module/page/extensions/navigation.py:115 +msgid "navigation extension" +msgstr "extensió de navegació" + +#: module/page/extensions/navigation.py:119 +msgid "" +"Select the module providing subpages for this page if you need to customize " +"the navigation." +msgstr "" +"Selecciona el mòdul que proveeix les subplanes si necessites personalitzar " +"la navegació." + +#: module/page/extensions/navigation.py:134 +msgid "Navigation extension" +msgstr "Extensió de navegació" + +#: module/page/extensions/navigationgroups.py:20 +msgid "Default" +msgstr "" + +#: module/page/extensions/navigationgroups.py:21 +msgid "Footer" +msgstr "" + +#: module/page/extensions/navigationgroups.py:28 +#, fuzzy +#| msgid "in navigation" +msgid "navigation group" +msgstr "a la navegació" + +#: module/page/extensions/relatedpages.py:21 +msgid "Select pages that should be listed as related content." +msgstr "Selecciones les planes que es mostraran com a contingut relacionat." + +#: module/page/extensions/relatedpages.py:26 +msgid "Related pages" +msgstr "Planes relacionades" + +#: module/page/extensions/sites.py:21 +msgid "Site" +msgstr "" + +#: module/page/extensions/symlinks.py:22 +msgid "symlinked page" +msgstr "Plana enllaçada" + +#: module/page/extensions/symlinks.py:23 +msgid "All content is inherited from this page if given." +msgstr "Tot el contingut es heretat d'aquesta plana." + +#: module/page/extensions/titles.py:19 +msgid "content title" +msgstr "títol del contingut" + +#: module/page/extensions/titles.py:22 +msgid "The first line is the main title, the following lines are subtitles." +msgstr "La primera línia és el títol principal, les altres són subtítols." + +#: module/page/extensions/titles.py:26 +msgid "page title" +msgstr "títol de la plana" + +#: module/page/extensions/titles.py:30 +#, fuzzy +#| msgid "Page title for browser window. Same as title by default." +msgid "" +"Page title for browser window. Same as title bydefault. Must not be longer " +"than 70 characters." +msgstr "Títol de la plana pel navegador. Per defecte el mateix que el títol." + +#: module/page/extensions/titles.py:60 +msgid "Titles" +msgstr "Títols" + +#: module/page/forms.py:187 msgid "This URL is already taken by an active page." msgstr "Aquesta URL ja està en ús en una plana activa." -#: module/page/forms.py:190 +#: module/page/forms.py:206 msgid "This URL is already taken by another active page." msgstr "Aquesta URL ja età en ús per una altra plana activa." -#: module/page/modeladmins.py:41 +#: module/page/forms.py:211 +msgid "This page does not allow attachment of child pages" +msgstr "" + +#: module/page/modeladmins.py:42 msgid "Other options" msgstr "Altres opcions" -#: module/page/modeladmins.py:90 module/page/models.py:174 +#: module/page/modeladmins.py:80 module/page/models.py:182 msgid "in navigation" msgstr "a la navegació" -#: module/page/modeladmins.py:105 +#: module/page/modeladmins.py:109 module/page/modeladmins.py:111 msgid "Add child page" msgstr "Afegeix una plana filla." -#: module/page/modeladmins.py:107 +#: module/page/modeladmins.py:120 module/page/modeladmins.py:122 #: templates/admin/feincms/content_inline.html:9 msgid "View on site" msgstr "Veure a Lloc" -#: module/page/modeladmins.py:126 +#: module/page/modeladmins.py:142 #, python-format -msgid "Add %(language)s Translation of \"%(page)s\"" +msgid "Add %(language)s translation of \"%(page)s\"" msgstr "" -#: module/page/modeladmins.py:156 +#: module/page/modeladmins.py:177 msgid "" "The content from the original translation has been copied to the newly " "created page." msgstr "" -#: module/page/modeladmins.py:170 +#: module/page/modeladmins.py:197 msgid "You don't have the necessary permissions to edit this object" msgstr "" -#: module/page/modeladmins.py:185 +#: module/page/modeladmins.py:223 msgid "inherited" msgstr "heretat" -#: module/page/modeladmins.py:189 +#: module/page/modeladmins.py:229 msgid "extensions" msgstr "extensions" -#: module/page/modeladmins.py:193 module/page/models.py:206 +#: module/page/modeladmins.py:233 module/page/models.py:221 msgid "is active" msgstr "és activa" -#: module/page/models.py:167 +#: module/page/models.py:169 msgid "active" msgstr "actiu" -#: module/page/models.py:175 +#: module/page/models.py:173 +#, fuzzy +#| msgid "This is used for the generated navigation too." +msgid "This title is also used for navigation menu items." +msgstr "Això també es fa servir per la navegació generada automàticament." + +#: module/page/models.py:176 +msgid "This is used to build the URL for this page" +msgstr "" + +#: module/page/models.py:184 msgid "override URL" msgstr "URL sobrescrit" -#: module/page/models.py:176 +#: module/page/models.py:186 msgid "" "Override the target URL. Be sure to include slashes at the beginning and at " -"the end if it is a local URL. This affects both the navigation and subpages'" -" URLs." -msgstr "Sobreescriu l'URL. Ha de contenir una '/' a l'inici i al final quan es tracti d'una URL local. Afecta tant a la navegació com a les URLs de les subplanes." +"the end if it is a local URL. This affects both the navigation and subpages' " +"URLs." +msgstr "" +"Sobreescriu l'URL. Ha de contenir una '/' a l'inici i al final quan es " +"tracti d'una URL local. Afecta tant a la navegació com a les URLs de les " +"subplanes." -#: module/page/models.py:177 +#: module/page/models.py:190 msgid "redirect to" msgstr "redirecciona a" -#: module/page/models.py:178 +#: module/page/models.py:193 msgid "Target URL for automatic redirects or the primary key of a page." msgstr "" -#: module/page/models.py:180 +#: module/page/models.py:196 msgid "Cached URL" msgstr "URL en caché" -#: module/page/models.py:395 +#: module/page/models.py:298 +#, python-format +msgid "" +"This %(page_class)s uses a singleton template, and " +"FEINCMS_SINGLETON_TEMPLATE_DELETION_ALLOWED=False" +msgstr "" + +#: module/page/models.py:426 msgid "page" msgstr "plana" -#: module/page/models.py:396 +#: module/page/models.py:427 msgid "pages" msgstr "planes" -#: module/page/extensions/excerpt.py:9 -msgid "excerpt" -msgstr "extracte" - -#: module/page/extensions/excerpt.py:10 -msgid "Add a brief excerpt summarizing the content of this page." -msgstr "Afegeix una breu descripció resumint el contingut de la plana" - -#: module/page/extensions/excerpt.py:12 -msgid "Excerpt" -msgstr "Extracte" - -#: module/page/extensions/navigation.py:82 -#: module/page/extensions/navigation.py:107 -msgid "navigation extension" -msgstr "extensió de navegació" - -#: module/page/extensions/navigation.py:110 -msgid "" -"Select the module providing subpages for this page if you need to customize " -"the navigation." -msgstr "Selecciona el mòdul que proveeix les subplanes si necessites personalitzar la navegació." - -#: module/page/extensions/navigation.py:125 -msgid "Navigation extension" -msgstr "Extensió de navegació" - -#: module/page/extensions/relatedpages.py:14 -msgid "Select pages that should be listed as related content." -msgstr "Selecciones les planes que es mostraran com a contingut relacionat." - -#: module/page/extensions/relatedpages.py:19 -msgid "Related pages" -msgstr "Planes relacionades" - -#: module/page/extensions/sites.py:16 -msgid "Site" -msgstr "" - -#: module/page/extensions/symlinks.py:15 -msgid "symlinked page" -msgstr "Plana enllaçada" - -#: module/page/extensions/symlinks.py:16 -msgid "All content is inherited from this page if given." -msgstr "Tot el contingut es heretat d'aquesta plana." - -#: module/page/extensions/titles.py:13 -msgid "content title" -msgstr "títol del contingut" - -#: module/page/extensions/titles.py:14 -msgid "The first line is the main title, the following lines are subtitles." -msgstr "La primera línia és el títol principal, les altres són subtítols." - -#: module/page/extensions/titles.py:15 -msgid "page title" -msgstr "títol de la plana" - -#: module/page/extensions/titles.py:16 -msgid "Page title for browser window. Same as title by default." -msgstr "Títol de la plana pel navegador. Per defecte el mateix que el títol." - -#: module/page/extensions/titles.py:43 -msgid "Titles" -msgstr "Títols" - -#: templates/admin/filter.html:3 -#, python-format -msgid " By %(filter_title)s " -msgstr "Per %(filter_title)s" - #: templates/admin/feincms/_messages_js.html:4 msgid "Really delete item?" msgstr "Segur que vols esborrar l'element?" @@ -757,7 +788,8 @@ msgstr "Impossible esborrar l'element" #: templates/admin/feincms/_messages_js.html:6 msgid "Cannot delete item, because it is parent of at least one other item." -msgstr "No puc esborrar l'element ja que és el pare d'almanco un altre element." +msgstr "" +"No puc esborrar l'element ja que és el pare d'almanco un altre element." #: templates/admin/feincms/_messages_js.html:7 msgid "Change template" @@ -770,9 +802,8 @@ msgstr "Vols canviar la plantilla?
    Tots els canvis es guardaran." #: templates/admin/feincms/_messages_js.html:9 #, python-format msgid "" -"Really change template?
    All changes are saved and content from " -"%%(source_regions)s is moved to " -"%%(target_region)s." +"Really change template?
    All changes are saved and content from " +"%%(source_regions)s is moved to %%(target_region)s." msgstr "" #: templates/admin/feincms/_messages_js.html:12 @@ -807,22 +838,24 @@ msgstr "Regió buida" msgid "" "Content from the parent site is automatically inherited. To override this " "behaviour, add some content." -msgstr "El contingut de la plana pare s'inserirà automàticament. Per sobreescriure aquest comportament afegeix algun contingut." +msgstr "" +"El contingut de la plana pare s'inserirà automàticament. Per sobreescriure " +"aquest comportament afegeix algun contingut." #: templates/admin/feincms/content_editor.html:33 msgid "Add new item" msgstr "Afegir un nou element" -#: templates/admin/feincms/content_inline.html:91 +#: templates/admin/feincms/content_inline.html:93 #, python-format msgid "Add another %(verbose_name)s" msgstr "" -#: templates/admin/feincms/content_inline.html:94 +#: templates/admin/feincms/content_inline.html:96 msgid "Remove" msgstr "" -#: templates/admin/feincms/fe_editor.html:40 +#: templates/admin/feincms/fe_editor.html:30 msgid "Save" msgstr "Guardar" @@ -850,13 +883,21 @@ msgstr "avall" msgid "remove" msgstr "esborrar" -#: templates/admin/feincms/recover_form.html:8 -#: templates/admin/feincms/revision_form.html:8 +#: templates/admin/feincms/page/page/item_editor.html:10 +msgid "Edit on site" +msgstr "Editar al Lloc" + #: templates/admin/feincms/page/page/item_editor.html:23 #: templates/admin/feincms/page/page/tree_editor.html:7 +#: templates/admin/feincms/recover_form.html:8 +#: templates/admin/feincms/revision_form.html:8 msgid "Home" msgstr "Inici" +#: templates/admin/feincms/page/page/item_editor.html:27 +msgid "Add" +msgstr "" + #: templates/admin/feincms/recover_form.html:11 #, python-format msgid "Recover deleted %(verbose_name)s" @@ -879,29 +920,26 @@ msgstr "" msgid "Press the save button below to revert to this version of the object." msgstr "" -#: templates/admin/feincms/tree_editor.html:32 +#: templates/admin/feincms/tree_editor.html:22 msgid "Shortcuts" msgstr "Dreceres" -#: templates/admin/feincms/tree_editor.html:34 +#: templates/admin/feincms/tree_editor.html:24 msgid "Collapse tree" msgstr "Tancar l'arbre" -#: templates/admin/feincms/tree_editor.html:35 +#: templates/admin/feincms/tree_editor.html:25 msgid "Expand tree" msgstr "Expandir l'arbre" -#: templates/admin/feincms/tree_editor.html:38 +#: templates/admin/feincms/tree_editor.html:29 msgid "Filter" msgstr "Filtrar" -#: templates/admin/feincms/page/page/item_editor.html:10 -msgid "Edit on site" -msgstr "Editar al Lloc" - -#: templates/admin/feincms/page/page/item_editor.html:27 -msgid "Add" -msgstr "" +#: templates/admin/filter.html:3 +#, python-format +msgid " By %(filter_title)s " +msgstr "Per %(filter_title)s" #: templates/admin/medialibrary/add_to_category.html:5 #: templates/admin/medialibrary/add_to_category.html:9 @@ -945,9 +983,13 @@ msgstr "%(comment_count)s comentaris." #, python-format msgid "" "\n" -" %(comment_username)s said on %(comment_submit_date)s
    \n" +" %(comment_username)s said on %(comment_submit_date)s
    \n" +" " +msgstr "" +"\n" +" %(comment_username)s digué el %(comment_submit_date)s
    \n" " " -msgstr "\n %(comment_username)s digué el %(comment_submit_date)s
    \n " #: templates/content/comments/default.html:28 msgid "No comments." @@ -964,3 +1006,27 @@ msgstr "Enviar" #: templates/content/contactform/thanks.html:3 msgid "Thanks!" msgstr "Gràcies!" + +#~ msgid "plain" +#~ msgstr "plà" + +#~ msgid "title row" +#~ msgstr "títol de la fila" + +#~ msgid "title row and column" +#~ msgstr "títol de fila i columna" + +#~ msgid "table" +#~ msgstr "taula" + +#~ msgid "tables" +#~ msgstr "taules" + +#~ msgid "data" +#~ msgstr "dades" + +#~ msgid "This will be prepended to the default keyword list." +#~ msgstr "Això serà afegit a la llista de paraules clau per defecte" + +#~ msgid "This will be prepended to the default description." +#~ msgstr "Això serà afegit a la descripció per defecte" diff --git a/feincms/locale/cs/LC_MESSAGES/django.po b/feincms/locale/cs/LC_MESSAGES/django.po index a099e2384..fea739112 100644 --- a/feincms/locale/cs/LC_MESSAGES/django.po +++ b/feincms/locale/cs/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2013-10-25 11:12+0200\n" +"POT-Creation-Date: 2014-07-20 14:59+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -17,193 +17,191 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2\n" -#: models.py:370 content/template/models.py:59 -msgid "template" -msgstr "šablona" - -#: models.py:510 -msgid "ordering" -msgstr "řazení" - -#: translations.py:259 module/blog/extensions/translations.py:17 -#: module/extensions/translations.py:123 -msgid "language" -msgstr "jazyk" - -#: admin/filterspecs.py:35 admin/filterspecs.py:70 +#: admin/filterspecs.py:47 admin/filterspecs.py:86 msgid "All" msgstr "vše" -#: admin/filterspecs.py:46 module/page/models.py:172 +#: admin/filterspecs.py:58 module/page/models.py:178 msgid "Parent" msgstr "rodič" -#: admin/filterspecs.py:81 +#: admin/filterspecs.py:97 #: templates/admin/medialibrary/mediafile/change_list.html:14 msgid "Category" msgstr "kategorie" -#: admin/item_editor.py:159 +#: admin/item_editor.py:190 #, python-format msgid "Change %s" msgstr "změň %s" -#: admin/tree_editor.py:234 content/rss/models.py:20 -#: content/section/models.py:34 module/blog/models.py:29 -#: module/medialibrary/models.py:40 module/page/models.py:170 -#: module/page/models.py:226 +#: admin/tree_editor.py:294 content/rss/models.py:23 +#: content/section/models.py:35 module/blog/models.py:35 +#: module/medialibrary/models.py:45 module/page/models.py:172 +#: module/page/models.py:240 msgid "title" msgstr "Název" -#: admin/tree_editor.py:407 +#: admin/tree_editor.py:353 admin/tree_editor.py:370 +#, fuzzy +#| msgid "You don't have the necessary permissions to edit this object" +msgid "You do not have permission to modify this object" +msgstr "Nemáte dostatečná oprávnění pro editaci tohoto objektu" + +#: admin/tree_editor.py:491 +msgid "No permission" +msgstr "" + +#: admin/tree_editor.py:509 #, python-format msgid "%s has been moved to a new position." msgstr "%s přesunut na novou pozici" -#: admin/tree_editor.py:411 +#: admin/tree_editor.py:512 msgid "Did not understand moving instruction." msgstr "Nesrozumitelné přesunutí" -#: admin/tree_editor.py:420 +#: admin/tree_editor.py:523 msgid "actions" msgstr "akce" -#: admin/tree_editor.py:432 -#, python-format -msgid "Successfully deleted %s items." -msgstr "" +#: admin/tree_editor.py:552 +#, fuzzy, python-format +#| msgid "Successfully added %(count)d media file to %(category)s." +#| msgid_plural "Successfully added %(count)d media files to %(category)s." +msgid "Successfully deleted %(count)d items." +msgstr "Úspěšně přidáno %(count)d mediálních souborů do %(category)s." -#: admin/tree_editor.py:440 +#: admin/tree_editor.py:565 #, fuzzy, python-format msgid "Delete selected %(verbose_name_plural)s" msgstr "Obnov smazané %(verbose_name)s" -#: content/application/models.py:218 +#: content/application/models.py:147 msgid "application content" msgstr "aplikační obsah" -#: content/application/models.py:219 +#: content/application/models.py:148 msgid "application contents" msgstr "aplikační obsahy" -#: content/application/models.py:249 +#: content/application/models.py:179 msgid "application" msgstr "aplikace" -#: content/comments/models.py:24 +#: content/comments/models.py:32 msgid "enabled" msgstr "zapnutý" -#: content/comments/models.py:24 +#: content/comments/models.py:33 msgid "New comments may be added" msgstr "Komentáře povoleny" -#: content/comments/models.py:28 content/comments/models.py:29 +#: content/comments/models.py:37 content/comments/models.py:38 msgid "comments" msgstr "komentáře" -#: content/comments/models.py:49 +#: content/comments/models.py:60 msgid "public" msgstr "veřejný" -#: content/comments/models.py:49 +#: content/comments/models.py:61 msgid "not public" msgstr "neveřejný" -#: content/contactform/models.py:18 +#: content/contactform/models.py:20 msgid "name" msgstr "jméno" -#: content/contactform/models.py:19 +#: content/contactform/models.py:21 msgid "email" msgstr "mail" -#: content/contactform/models.py:20 +#: content/contactform/models.py:22 msgid "subject" msgstr "předmět" -#: content/contactform/models.py:23 content/raw/models.py:14 +#: content/contactform/models.py:26 content/raw/models.py:16 msgid "content" msgstr "obsah" -#: content/contactform/models.py:34 +#: content/contactform/models.py:37 msgid "contact form" msgstr "kontaktní formulář" -#: content/contactform/models.py:35 +#: content/contactform/models.py:38 msgid "contact forms" msgstr "kontaktní formuláře" -#: content/file/models.py:20 content/file/models.py:25 -#: module/medialibrary/models.py:84 +#: content/file/models.py:23 content/file/models.py:28 +#: module/medialibrary/models.py:94 msgid "file" msgstr "soubor" -#: content/file/models.py:26 +#: content/file/models.py:29 msgid "files" msgstr "soubory" -#: content/image/models.py:45 content/image/models.py:54 +#: content/image/models.py:46 content/image/models.py:55 msgid "image" msgstr "obrázek" -#: content/image/models.py:48 +#: content/image/models.py:49 #, fuzzy msgid "alternate text" msgstr "šablonový obsah" -#: content/image/models.py:49 +#: content/image/models.py:50 #, fuzzy msgid "Description of image" msgstr "popis" -#: content/image/models.py:50 module/medialibrary/models.py:231 +#: content/image/models.py:51 module/medialibrary/models.py:260 msgid "caption" msgstr "název" -#: content/image/models.py:55 +#: content/image/models.py:56 msgid "images" msgstr "obrázky" -#: content/image/models.py:80 +#: content/image/models.py:82 msgid "position" msgstr "pozice" -#: content/image/models.py:88 +#: content/image/models.py:90 msgid "format" msgstr "" -#: content/medialibrary/models.py:49 content/section/models.py:36 -#: module/medialibrary/fields.py:59 module/medialibrary/models.py:97 +#: content/medialibrary/models.py:51 content/section/models.py:38 +#: module/medialibrary/fields.py:66 module/medialibrary/models.py:112 msgid "media file" msgstr "mediální soubor" -#: content/medialibrary/models.py:50 module/medialibrary/models.py:98 +#: content/medialibrary/models.py:52 module/medialibrary/models.py:113 msgid "media files" msgstr "mediální soubory" -#: content/medialibrary/models.py:58 content/section/models.py:52 -#: content/table/models.py:82 +#: content/medialibrary/models.py:64 content/section/models.py:59 msgid "type" msgstr "typ" -#: content/raw/models.py:18 +#: content/raw/models.py:20 msgid "raw content" msgstr "binární obsah" -#: content/raw/models.py:19 +#: content/raw/models.py:21 msgid "raw contents" msgstr "binární obsahy" -#: content/richtext/models.py:22 +#: content/richtext/models.py:24 msgid "HTML Tidy" msgstr "čisté HTML " -#: content/richtext/models.py:23 +#: content/richtext/models.py:25 msgid "Ignore the HTML validation warnings" msgstr "ignorovat HTML validační varovnání" -#: content/richtext/models.py:47 +#: content/richtext/models.py:55 #, python-format msgid "" "HTML validation produced %(count)d warnings. Please review the updated " @@ -212,19 +210,19 @@ msgstr "" "HTML validace vyplivla %(count)d varování. Prosím zkontroluj obsah níže před " "pokračováním: %(messages)s" -#: content/richtext/models.py:84 content/section/models.py:35 +#: content/richtext/models.py:99 content/section/models.py:36 msgid "text" msgstr "text" -#: content/richtext/models.py:88 +#: content/richtext/models.py:103 msgid "rich text" msgstr "" -#: content/richtext/models.py:89 +#: content/richtext/models.py:104 msgid "rich texts" msgstr "" -#: content/rss/models.py:21 +#: content/rss/models.py:25 msgid "" "The rss field is updated several times a day. A change in the title will " "only be visible on the home page after the next feed update." @@ -232,75 +230,55 @@ msgstr "" "RSS pole je měněno několikrát za den. Změna v titulku bude patrná při přístí " "změně RSS kanálu." -#: content/rss/models.py:22 +#: content/rss/models.py:28 msgid "link" msgstr "odkaz" -#: content/rss/models.py:23 +#: content/rss/models.py:30 msgid "pre-rendered content" msgstr "predrenderovaný obsah" -#: content/rss/models.py:24 +#: content/rss/models.py:32 msgid "last updated" msgstr "naposledy změněno" -#: content/rss/models.py:25 +#: content/rss/models.py:33 msgid "max. items" msgstr "max. položek" -#: content/rss/models.py:29 +#: content/rss/models.py:37 msgid "RSS feed" msgstr "RSS kanál" -#: content/rss/models.py:30 +#: content/rss/models.py:38 msgid "RSS feeds" msgstr "RSS kanály" -#: content/section/models.py:41 +#: content/section/models.py:43 msgid "section" msgstr "sekce" -#: content/section/models.py:42 +#: content/section/models.py:44 msgid "sections" msgstr "sekce" -#: content/table/models.py:63 -msgid "plain" -msgstr "prostý" - -#: content/table/models.py:64 -msgid "title row" -msgstr "titulní řádka" - -#: content/table/models.py:66 -msgid "title row and column" -msgstr "titulní řádka a sloupec" - -#: content/table/models.py:72 -msgid "table" -msgstr "tabulka" - -#: content/table/models.py:73 -msgid "tables" -msgstr "tabulky" - -#: content/table/models.py:87 -msgid "data" -msgstr "" - -#: content/template/models.py:51 +#: content/template/models.py:53 msgid "template content" msgstr "šablonový obsah" -#: content/template/models.py:52 +#: content/template/models.py:54 msgid "template contents" msgstr "šablonové obsahy" -#: content/video/models.py:31 +#: content/template/models.py:63 models.py:400 +msgid "template" +msgstr "šablona" + +#: content/video/models.py:34 msgid "video link" msgstr "odkaz na video" -#: content/video/models.py:32 +#: content/video/models.py:36 msgid "" "This should be a link to a youtube or vimeo video, i.e.: http://www.youtube." "com/watch?v=zmj1rpzDRZ0" @@ -308,139 +286,150 @@ msgstr "" "Toto má být odkaz na youtube nebo vimeo video, např. http://www.youtube.com/" "watch?v=zmj1rpzDRZ0" -#: content/video/models.py:37 +#: content/video/models.py:41 msgid "video" msgstr "video" -#: content/video/models.py:38 +#: content/video/models.py:42 msgid "videos" msgstr "videoa" -#: contrib/tagging.py:117 +#: contrib/tagging.py:132 msgid "Tagging" msgstr "Tagy" -#: module/blog/models.py:28 +#: models.py:550 +msgid "ordering" +msgstr "řazení" + +#: module/blog/extensions/tags.py:17 +msgid "tags" +msgstr "tagy" + +#: module/blog/extensions/translations.py:25 +#: module/extensions/translations.py:140 translations.py:282 +msgid "language" +msgstr "jazyk" + +#: module/blog/extensions/translations.py:35 +#: module/extensions/translations.py:148 +msgid "translation of" +msgstr "překlad" + +#: module/blog/extensions/translations.py:39 +#: module/extensions/translations.py:152 +msgid "Leave this empty for entries in the primary language." +msgstr "Nech prázdné pro články v primárním jazyce" + +#: module/blog/extensions/translations.py:67 +#: templates/admin/feincms/item_editor.html:33 +msgid "available translations" +msgstr "překlady k dispozici" + +#: module/blog/models.py:33 msgid "published" msgstr "publikovaný" -#: module/blog/models.py:30 +#: module/blog/models.py:36 msgid "This is used for the generated navigation too." msgstr "Toto je také použito pro generovanou navigaci" -#: module/blog/models.py:33 +#: module/blog/models.py:40 msgid "published on" msgstr "publikovaný" -#: module/blog/models.py:34 +#: module/blog/models.py:42 msgid "Will be set automatically once you tick the `published` checkbox above." msgstr "" "Bude automaticky nastaven jakmile klikneš na zaškrtávátko `publikovaný` výše." -#: module/blog/models.py:39 +#: module/blog/models.py:48 msgid "entry" msgstr "Článek" -#: module/blog/models.py:40 +#: module/blog/models.py:49 msgid "entries" msgstr "Články" -#: module/blog/extensions/tags.py:12 -msgid "tags" -msgstr "tagy" - -#: module/blog/extensions/translations.py:20 -#: module/extensions/translations.py:126 -msgid "translation of" -msgstr "překlad" - -#: module/blog/extensions/translations.py:23 -#: module/extensions/translations.py:129 -msgid "Leave this empty for entries in the primary language." -msgstr "Nech prázdné pro články v primárním jazyce" - -#: module/blog/extensions/translations.py:44 -#: templates/admin/feincms/item_editor.html:43 -msgid "available translations" -msgstr "překlady k dispozici" - -#: module/extensions/changedate.py:32 +#: module/extensions/changedate.py:41 msgid "creation date" msgstr "datum vytvoření" -#: module/extensions/changedate.py:33 +#: module/extensions/changedate.py:43 msgid "modification date" msgstr "datum úpravy" -#: module/extensions/ct_tracker.py:140 +#: module/extensions/ct_tracker.py:156 msgid "content types" msgstr "typy obsahu" -#: module/extensions/datepublisher.py:67 +#: module/extensions/datepublisher.py:86 msgid "publication date" msgstr "datum publikace" -#: module/extensions/datepublisher.py:70 +#: module/extensions/datepublisher.py:90 msgid "publication end date" msgstr "konec publikace" -#: module/extensions/datepublisher.py:72 +#: module/extensions/datepublisher.py:93 msgid "Leave empty if the entry should stay active forever." msgstr "Nech prázdné pro články, které mají být publikovány stále" -#: module/extensions/datepublisher.py:103 +#: module/extensions/datepublisher.py:128 msgid "visible from - to" msgstr "Publikováno od - do" -#: module/extensions/datepublisher.py:113 +#: module/extensions/datepublisher.py:139 msgid "Date-based publishing" msgstr "Publikování na základě data" -#: module/extensions/featured.py:9 +#: module/extensions/featured.py:15 msgid "featured" msgstr "zvýrazněný" -#: module/extensions/featured.py:14 +#: module/extensions/featured.py:21 msgid "Featured" msgstr "zvýrazněný" -#: module/extensions/seo.py:9 +#: module/extensions/seo.py:16 msgid "meta keywords" msgstr "meta klíčová slova" -#: module/extensions/seo.py:10 -msgid "This will be prepended to the default keyword list." -msgstr "Toto bude přidáno k výchozím klíčovým slovům" +#: module/extensions/seo.py:18 +msgid "Keywords are ignored by most search engines." +msgstr "" -#: module/extensions/seo.py:11 +#: module/extensions/seo.py:20 msgid "meta description" msgstr "meta popis" -#: module/extensions/seo.py:12 -msgid "This will be prepended to the default description." -msgstr "Toto bude přidáno k výchozímu popisu" +#: module/extensions/seo.py:22 +msgid "" +"This text is displayed on the search results page. It is however not used " +"for the SEO ranking. Text longer than 140 characters is truncated." +msgstr "" -#: module/extensions/seo.py:17 +#: module/extensions/seo.py:32 msgid "Search engine optimization" msgstr "Optimalizace pro vyhledávače" -#: module/extensions/translations.py:207 +#: module/extensions/translations.py:249 msgid "Edit translation" msgstr "" -#: module/extensions/translations.py:210 +#: module/extensions/translations.py:256 msgid "Create translation" msgstr "Vytvoř překlad" -#: module/extensions/translations.py:215 +#: module/extensions/translations.py:264 msgid "translations" msgstr "překlady" -#: module/medialibrary/forms.py:26 +#: module/medialibrary/forms.py:31 msgid "This would create a loop in the hierarchy" msgstr "" -#: module/medialibrary/forms.py:64 +#: module/medialibrary/forms.py:77 #, python-format msgid "" "Cannot overwrite with different file type (attempt to overwrite a " @@ -449,207 +438,308 @@ msgstr "" "Nemůže být přepsán jiným typem (pokoušíš se přepsat %(old_ext)s s " "%(new_ext)s)" -#: module/medialibrary/modeladmins.py:58 +#: module/medialibrary/modeladmins.py:63 #, python-format msgid "Successfully added %(count)d media file to %(category)s." msgid_plural "Successfully added %(count)d media files to %(category)s." msgstr[0] "Úspěšně přidáno %(count)d mediálních souborů do %(category)s." msgstr[1] "Úspěšně přidáno %(count)d mediálních souborů do %(category)s." -#: module/medialibrary/modeladmins.py:77 +#: module/medialibrary/modeladmins.py:84 msgid "Add selected media files to category" msgstr "Přiděj vybrané mediální soubory do kategorie" -#: module/medialibrary/modeladmins.py:86 +#: module/medialibrary/modeladmins.py:94 #, python-format msgid "ZIP file exported as %s" msgstr "ZIP archiv exporotován jako %s" -#: module/medialibrary/modeladmins.py:88 +#: module/medialibrary/modeladmins.py:96 #, python-format msgid "ZIP file export failed: %s" msgstr "export ZIP archivu selhal: %s" -#: module/medialibrary/modeladmins.py:93 +#: module/medialibrary/modeladmins.py:104 msgid "Export selected media files as zip file" msgstr "Vyexportuj vybrané mediální soubory jako zip archiv" -#: module/medialibrary/modeladmins.py:136 +#: module/medialibrary/modeladmins.py:157 #: templates/admin/feincms/page/page/item_editor.html:15 msgid "Preview" msgstr "náhled" -#: module/medialibrary/modeladmins.py:141 module/medialibrary/models.py:88 +#: module/medialibrary/modeladmins.py:162 module/medialibrary/models.py:103 msgid "file size" msgstr "velikost souboru" -#: module/medialibrary/modeladmins.py:146 module/medialibrary/models.py:86 +#: module/medialibrary/modeladmins.py:167 module/medialibrary/models.py:100 msgid "created" msgstr "vytvoreno" -#: module/medialibrary/modeladmins.py:165 module/medialibrary/models.py:85 +#: module/medialibrary/modeladmins.py:187 module/medialibrary/models.py:97 msgid "file type" msgstr "typ souboru" -#: module/medialibrary/modeladmins.py:186 +#: module/medialibrary/modeladmins.py:211 msgid "file info" msgstr "info o souboru" -#: module/medialibrary/modeladmins.py:198 +#: module/medialibrary/modeladmins.py:224 #, python-format msgid "%d files imported" msgstr "%d souborů importováno" -#: module/medialibrary/modeladmins.py:200 +#: module/medialibrary/modeladmins.py:226 #, python-format msgid "ZIP import failed: %s" msgstr "import ZIPu selhal: %s" -#: module/medialibrary/modeladmins.py:202 +#: module/medialibrary/modeladmins.py:228 msgid "No input file given" msgstr "Žádný vstupní soubor" -#: module/medialibrary/models.py:43 +#: module/medialibrary/models.py:49 msgid "parent" msgstr "rodič" -#: module/medialibrary/models.py:45 module/page/models.py:171 +#: module/medialibrary/models.py:51 module/page/models.py:175 msgid "slug" msgstr "" -#: module/medialibrary/models.py:49 +#: module/medialibrary/models.py:55 msgid "category" msgstr "kategorie" -#: module/medialibrary/models.py:50 module/medialibrary/models.py:90 +#: module/medialibrary/models.py:56 module/medialibrary/models.py:106 msgid "categories" msgstr "kategorie" -#: module/medialibrary/models.py:87 +#: module/medialibrary/models.py:101 msgid "copyright" msgstr "" -#: module/medialibrary/models.py:202 +#: module/medialibrary/models.py:219 msgid "Image" msgstr "obrázek" -#: module/medialibrary/models.py:203 +#: module/medialibrary/models.py:221 msgid "Video" msgstr "" -#: module/medialibrary/models.py:204 +#: module/medialibrary/models.py:224 msgid "Audio" msgstr "" -#: module/medialibrary/models.py:205 +#: module/medialibrary/models.py:226 msgid "PDF document" msgstr "PDF dokument" -#: module/medialibrary/models.py:206 +#: module/medialibrary/models.py:227 msgid "Flash" msgstr "" -#: module/medialibrary/models.py:207 +#: module/medialibrary/models.py:228 msgid "Text" msgstr "" -#: module/medialibrary/models.py:208 +#: module/medialibrary/models.py:229 msgid "Rich Text" msgstr "" -#: module/medialibrary/models.py:209 +#: module/medialibrary/models.py:230 msgid "Zip archive" msgstr "" -#: module/medialibrary/models.py:210 +#: module/medialibrary/models.py:231 msgid "Microsoft Word" msgstr "" -#: module/medialibrary/models.py:211 +#: module/medialibrary/models.py:233 msgid "Microsoft Excel" msgstr "" -#: module/medialibrary/models.py:212 +#: module/medialibrary/models.py:235 msgid "Microsoft PowerPoint" msgstr "" -#: module/medialibrary/models.py:213 +#: module/medialibrary/models.py:237 msgid "Binary" msgstr "Binární" -#: module/medialibrary/models.py:232 +#: module/medialibrary/models.py:261 msgid "description" msgstr "popis" -#: module/medialibrary/models.py:235 +#: module/medialibrary/models.py:264 msgid "media file translation" msgstr "překlad mediálního souboru" -#: module/medialibrary/models.py:236 +#: module/medialibrary/models.py:265 msgid "media file translations" msgstr "překlady mediálního souboru" -#: module/page/forms.py:172 +#: module/page/extensions/excerpt.py:18 +msgid "excerpt" +msgstr "" + +#: module/page/extensions/excerpt.py:21 +msgid "Add a brief excerpt summarizing the content of this page." +msgstr "" + +#: module/page/extensions/excerpt.py:25 +msgid "Excerpt" +msgstr "výňatek" + +#: module/page/extensions/navigation.py:86 +#: module/page/extensions/navigation.py:115 +msgid "navigation extension" +msgstr "rozšíření navigace" + +#: module/page/extensions/navigation.py:119 +msgid "" +"Select the module providing subpages for this page if you need to customize " +"the navigation." +msgstr "" +"Vyber modul nabízející podstránky pro tuto stránku jestli chceš upravit " +"navigaci." + +#: module/page/extensions/navigation.py:134 +msgid "Navigation extension" +msgstr "rozšíření navigace" + +#: module/page/extensions/navigationgroups.py:20 +msgid "Default" +msgstr "" + +#: module/page/extensions/navigationgroups.py:21 +msgid "Footer" +msgstr "" + +#: module/page/extensions/navigationgroups.py:28 +#, fuzzy +#| msgid "in navigation" +msgid "navigation group" +msgstr "v navigaci" + +#: module/page/extensions/relatedpages.py:21 +msgid "Select pages that should be listed as related content." +msgstr "Vyber příbuzné stránky" + +#: module/page/extensions/relatedpages.py:26 +msgid "Related pages" +msgstr "Příbuzné stránky" + +#: module/page/extensions/sites.py:21 +msgid "Site" +msgstr "" + +#: module/page/extensions/symlinks.py:22 +msgid "symlinked page" +msgstr "propojená stránka" + +#: module/page/extensions/symlinks.py:23 +msgid "All content is inherited from this page if given." +msgstr "Všechen obsah je poděděn do této stránky, pakliže vybrána" + +#: module/page/extensions/titles.py:19 +msgid "content title" +msgstr "Název obsahu" + +#: module/page/extensions/titles.py:22 +msgid "The first line is the main title, the following lines are subtitles." +msgstr "První řádka je hlavní titulek, ostatní řádky podtitulky" + +#: module/page/extensions/titles.py:26 +msgid "page title" +msgstr "titulek stránky" + +#: module/page/extensions/titles.py:30 +#, fuzzy +#| msgid "Page title for browser window. Same as title by default." +msgid "" +"Page title for browser window. Same as title bydefault. Must not be longer " +"than 70 characters." +msgstr "Titulek pro okno prohlížeče. Výchozí: stejné jako titulek stránky." + +#: module/page/extensions/titles.py:60 +msgid "Titles" +msgstr "Titulky" + +#: module/page/forms.py:187 msgid "This URL is already taken by an active page." msgstr "Toto URL je už obsazeno aktivní stránkou." -#: module/page/forms.py:190 +#: module/page/forms.py:206 msgid "This URL is already taken by another active page." msgstr "Toto URL je už obsazeno jinou stránkou." -#: module/page/modeladmins.py:41 +#: module/page/forms.py:211 +msgid "This page does not allow attachment of child pages" +msgstr "" + +#: module/page/modeladmins.py:42 msgid "Other options" msgstr "Další nastavení" -#: module/page/modeladmins.py:90 module/page/models.py:174 +#: module/page/modeladmins.py:80 module/page/models.py:182 msgid "in navigation" msgstr "v navigaci" -#: module/page/modeladmins.py:105 +#: module/page/modeladmins.py:109 module/page/modeladmins.py:111 msgid "Add child page" msgstr "Přidej podřízenou stránku" -#: module/page/modeladmins.py:107 +#: module/page/modeladmins.py:120 module/page/modeladmins.py:122 #: templates/admin/feincms/content_inline.html:9 msgid "View on site" msgstr "Zobrazena na sajtě." -#: module/page/modeladmins.py:126 +#: module/page/modeladmins.py:142 #, python-format -msgid "Add %(language)s Translation of \"%(page)s\"" +msgid "Add %(language)s translation of \"%(page)s\"" msgstr "" -#: module/page/modeladmins.py:156 +#: module/page/modeladmins.py:177 msgid "" "The content from the original translation has been copied to the newly " "created page." msgstr "Originálnío překlad zkopírován do nově vytvořené stránky." -#: module/page/modeladmins.py:170 +#: module/page/modeladmins.py:197 msgid "You don't have the necessary permissions to edit this object" msgstr "Nemáte dostatečná oprávnění pro editaci tohoto objektu" -#: module/page/modeladmins.py:185 +#: module/page/modeladmins.py:223 msgid "inherited" msgstr "zděděný" -#: module/page/modeladmins.py:189 +#: module/page/modeladmins.py:229 msgid "extensions" msgstr "rozšíření" -#: module/page/modeladmins.py:193 module/page/models.py:206 +#: module/page/modeladmins.py:233 module/page/models.py:221 msgid "is active" msgstr "je aktivní" -#: module/page/models.py:167 +#: module/page/models.py:169 msgid "active" msgstr "aktivní" -#: module/page/models.py:175 +#: module/page/models.py:173 +#, fuzzy +#| msgid "This is used for the generated navigation too." +msgid "This title is also used for navigation menu items." +msgstr "Toto je také použito pro generovanou navigaci" + +#: module/page/models.py:176 +msgid "This is used to build the URL for this page" +msgstr "" + +#: module/page/models.py:184 msgid "override URL" msgstr "vynucené URL" -#: module/page/models.py:176 +#: module/page/models.py:186 msgid "" "Override the target URL. Be sure to include slashes at the beginning and at " "the end if it is a local URL. This affects both the navigation and subpages' " @@ -658,101 +748,34 @@ msgstr "" "Přepíše cílové URL. Ujisti se, že máš \"/\" na začátku a na konci pro " "lokální URL.Toto ovlivňuje navigaci cílové stránky i podstránek" -#: module/page/models.py:177 +#: module/page/models.py:190 msgid "redirect to" msgstr "přesměruj na" -#: module/page/models.py:178 +#: module/page/models.py:193 #, fuzzy msgid "Target URL for automatic redirects or the primary key of a page." msgstr "Cílová adresa pro automatické přeměrování" -#: module/page/models.py:180 +#: module/page/models.py:196 msgid "Cached URL" msgstr "" -#: module/page/models.py:395 +#: module/page/models.py:298 +#, python-format +msgid "" +"This %(page_class)s uses a singleton template, and " +"FEINCMS_SINGLETON_TEMPLATE_DELETION_ALLOWED=False" +msgstr "" + +#: module/page/models.py:426 msgid "page" msgstr "stránka" -#: module/page/models.py:396 +#: module/page/models.py:427 msgid "pages" msgstr "stránky" -#: module/page/extensions/excerpt.py:9 -msgid "excerpt" -msgstr "" - -#: module/page/extensions/excerpt.py:10 -msgid "Add a brief excerpt summarizing the content of this page." -msgstr "" - -#: module/page/extensions/excerpt.py:12 -msgid "Excerpt" -msgstr "výňatek" - -#: module/page/extensions/navigation.py:82 -#: module/page/extensions/navigation.py:107 -msgid "navigation extension" -msgstr "rozšíření navigace" - -#: module/page/extensions/navigation.py:110 -msgid "" -"Select the module providing subpages for this page if you need to customize " -"the navigation." -msgstr "" -"Vyber modul nabízející podstránky pro tuto stránku jestli chceš upravit " -"navigaci." - -#: module/page/extensions/navigation.py:125 -msgid "Navigation extension" -msgstr "rozšíření navigace" - -#: module/page/extensions/relatedpages.py:14 -msgid "Select pages that should be listed as related content." -msgstr "Vyber příbuzné stránky" - -#: module/page/extensions/relatedpages.py:19 -msgid "Related pages" -msgstr "Příbuzné stránky" - -#: module/page/extensions/sites.py:16 -msgid "Site" -msgstr "" - -#: module/page/extensions/symlinks.py:15 -msgid "symlinked page" -msgstr "propojená stránka" - -#: module/page/extensions/symlinks.py:16 -msgid "All content is inherited from this page if given." -msgstr "Všechen obsah je poděděn do této stránky, pakliže vybrána" - -#: module/page/extensions/titles.py:13 -msgid "content title" -msgstr "Název obsahu" - -#: module/page/extensions/titles.py:14 -msgid "The first line is the main title, the following lines are subtitles." -msgstr "První řádka je hlavní titulek, ostatní řádky podtitulky" - -#: module/page/extensions/titles.py:15 -msgid "page title" -msgstr "titulek stránky" - -#: module/page/extensions/titles.py:16 -msgid "Page title for browser window. Same as title by default." -msgstr "Titulek pro okno prohlížeče. Výchozí: stejné jako titulek stránky." - -#: module/page/extensions/titles.py:43 -msgid "Titles" -msgstr "Titulky" - -#: templates/admin/filter.html:3 -#, python-format -msgid " By %(filter_title)s " -msgstr "" - #: templates/admin/feincms/_messages_js.html:4 msgid "Really delete item?" msgstr "" @@ -831,16 +854,16 @@ msgstr "" msgid "Add new item" msgstr "Přidej novou položku" -#: templates/admin/feincms/content_inline.html:91 +#: templates/admin/feincms/content_inline.html:93 #, python-format msgid "Add another %(verbose_name)s" msgstr "Přidej nové %(verbose_name)s" -#: templates/admin/feincms/content_inline.html:94 +#: templates/admin/feincms/content_inline.html:96 msgid "Remove" msgstr "Smaž" -#: templates/admin/feincms/fe_editor.html:40 +#: templates/admin/feincms/fe_editor.html:30 msgid "Save" msgstr "Ulož" @@ -868,13 +891,21 @@ msgstr "dolů" msgid "remove" msgstr "odeber" -#: templates/admin/feincms/recover_form.html:8 -#: templates/admin/feincms/revision_form.html:8 +#: templates/admin/feincms/page/page/item_editor.html:10 +msgid "Edit on site" +msgstr "Edituj přímo na webu" + #: templates/admin/feincms/page/page/item_editor.html:23 #: templates/admin/feincms/page/page/tree_editor.html:7 +#: templates/admin/feincms/recover_form.html:8 +#: templates/admin/feincms/revision_form.html:8 msgid "Home" msgstr "Domů" +#: templates/admin/feincms/page/page/item_editor.html:27 +msgid "Add" +msgstr "Přidej" + #: templates/admin/feincms/recover_form.html:11 #, python-format msgid "Recover deleted %(verbose_name)s" @@ -897,29 +928,26 @@ msgstr "Revertuj %(verbose_name)s" msgid "Press the save button below to revert to this version of the object." msgstr "Zmáčkni tlačítko ulož níže pro revertování na tuto verzi objektu." -#: templates/admin/feincms/tree_editor.html:32 +#: templates/admin/feincms/tree_editor.html:22 msgid "Shortcuts" msgstr "Zkratky" -#: templates/admin/feincms/tree_editor.html:34 +#: templates/admin/feincms/tree_editor.html:24 msgid "Collapse tree" msgstr "Rozbal strom" -#: templates/admin/feincms/tree_editor.html:35 +#: templates/admin/feincms/tree_editor.html:25 msgid "Expand tree" msgstr "Zabal strom" -#: templates/admin/feincms/tree_editor.html:38 +#: templates/admin/feincms/tree_editor.html:29 msgid "Filter" msgstr "Filtruj" -#: templates/admin/feincms/page/page/item_editor.html:10 -msgid "Edit on site" -msgstr "Edituj přímo na webu" - -#: templates/admin/feincms/page/page/item_editor.html:27 -msgid "Add" -msgstr "Přidej" +#: templates/admin/filter.html:3 +#, python-format +msgid " By %(filter_title)s " +msgstr "" #: templates/admin/medialibrary/add_to_category.html:5 #: templates/admin/medialibrary/add_to_category.html:9 @@ -984,6 +1012,27 @@ msgstr "Odeslat" msgid "Thanks!" msgstr "Díky!" +#~ msgid "plain" +#~ msgstr "prostý" + +#~ msgid "title row" +#~ msgstr "titulní řádka" + +#~ msgid "title row and column" +#~ msgstr "titulní řádka a sloupec" + +#~ msgid "table" +#~ msgstr "tabulka" + +#~ msgid "tables" +#~ msgstr "tabulky" + +#~ msgid "This will be prepended to the default keyword list." +#~ msgstr "Toto bude přidáno k výchozím klíčovým slovům" + +#~ msgid "This will be prepended to the default description." +#~ msgstr "Toto bude přidáno k výchozímu popisu" + #~ msgid "(no caption)" #~ msgstr "(bez názvu)" diff --git a/feincms/locale/de/LC_MESSAGES/django.mo b/feincms/locale/de/LC_MESSAGES/django.mo index deda58df1..764b5548c 100644 Binary files a/feincms/locale/de/LC_MESSAGES/django.mo and b/feincms/locale/de/LC_MESSAGES/django.mo differ diff --git a/feincms/locale/de/LC_MESSAGES/django.po b/feincms/locale/de/LC_MESSAGES/django.po index 2f3530775..3d165754f 100644 --- a/feincms/locale/de/LC_MESSAGES/django.po +++ b/feincms/locale/de/LC_MESSAGES/django.po @@ -1,745 +1,606 @@ # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. -# +# # Translators: # Matthias Kestenholz , 2011 # sbaechler , 2013 msgid "" msgstr "" "Project-Id-Version: FeinCMS\n" -"Report-Msgid-Bugs-To: http://github.com/feincms/feincms/issues\n" -"POT-Creation-Date: 2013-10-25 11:12+0200\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2015-09-02 22:19+0200\n" "PO-Revision-Date: 2013-10-25 09:15+0000\n" "Last-Translator: Matthias Kestenholz \n" -"Language-Team: German (http://www.transifex.com/projects/p/feincms/language/de/)\n" +"Language-Team: German (http://www.transifex.com/projects/p/feincms/language/" +"de/)\n" +"Language: de\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Language: de\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: models.py:370 content/template/models.py:59 -msgid "template" -msgstr "Template" - -#: models.py:510 -msgid "ordering" -msgstr "Sortierung" - -#: translations.py:259 module/blog/extensions/translations.py:17 -#: module/extensions/translations.py:123 -msgid "language" -msgstr "Sprache" - -#: admin/filterspecs.py:35 admin/filterspecs.py:70 +#: admin/filters.py:49 admin/filters.py:97 msgid "All" msgstr "Alle" -#: admin/filterspecs.py:46 module/page/models.py:172 +#: admin/filters.py:60 module/page/models.py:165 msgid "Parent" msgstr "Übergeordnet" -#: admin/filterspecs.py:81 -#: templates/admin/medialibrary/mediafile/change_list.html:14 +#: admin/filters.py:108 +#: templates/admin/medialibrary/mediafile/change_list.html:13 msgid "Category" msgstr "Kategorie" -#: admin/item_editor.py:159 -#, python-format -msgid "Change %s" -msgstr "%s ändern" - -#: admin/tree_editor.py:234 content/rss/models.py:20 -#: content/section/models.py:34 module/blog/models.py:29 -#: module/medialibrary/models.py:40 module/page/models.py:170 -#: module/page/models.py:226 +#: admin/tree_editor.py:290 module/medialibrary/models.py:45 +#: module/page/models.py:159 module/page/models.py:231 msgid "title" msgstr "Titel" -#: admin/tree_editor.py:407 +#: admin/tree_editor.py:349 admin/tree_editor.py:366 +msgid "You do not have permission to modify this object" +msgstr "" +"Sie haben die nötige Berechtigung nicht, um dieses Objekt zu bearbeiten" + +#: admin/tree_editor.py:482 +msgid "No permission" +msgstr "Keine Berechtigung" + +#: admin/tree_editor.py:500 #, python-format msgid "%s has been moved to a new position." msgstr "%s wurde an eine neue Position verschoben." -#: admin/tree_editor.py:411 +#: admin/tree_editor.py:503 msgid "Did not understand moving instruction." msgstr "Unbekannter Verschiebe-Befehl." -#: admin/tree_editor.py:420 +#: admin/tree_editor.py:514 msgid "actions" msgstr "Aktionen" -#: admin/tree_editor.py:432 +#: admin/tree_editor.py:543 #, python-format -msgid "Successfully deleted %s items." -msgstr "%s Einträge gelöscht." +#| msgid "Successfully deleted %(count)d items." +msgid "Successfully deleted %(count)d items." +msgstr "Erfolgreich %(count)d Einträge gelöscht." -#: admin/tree_editor.py:440 +#: admin/tree_editor.py:556 #, python-format msgid "Delete selected %(verbose_name_plural)s" msgstr "Ausgewählte %(verbose_name_plural)s löschen" -#: content/application/models.py:218 +#: apps/contents.py:32 msgid "application content" msgstr "Applikation" -#: content/application/models.py:219 +#: apps/contents.py:33 msgid "application contents" msgstr "Applikationen" -#: content/application/models.py:249 +#: apps/contents.py:64 msgid "application" msgstr "Applikation" -#: content/comments/models.py:24 -msgid "enabled" -msgstr "aktiviert" - -#: content/comments/models.py:24 -msgid "New comments may be added" -msgstr "Neue Kommentare können hinzugefügt werden" - -#: content/comments/models.py:28 content/comments/models.py:29 -msgid "comments" -msgstr "Kommentare" - -#: content/comments/models.py:49 -msgid "public" -msgstr "veröffentlicht" - -#: content/comments/models.py:49 -msgid "not public" -msgstr "nicht öffentlich" - -#: content/contactform/models.py:18 -msgid "name" -msgstr "Name" - -#: content/contactform/models.py:19 -msgid "email" -msgstr "E-Mail" - -#: content/contactform/models.py:20 -msgid "subject" -msgstr "Betreff" - -#: content/contactform/models.py:23 content/raw/models.py:14 -msgid "content" -msgstr "Inhalt" - -#: content/contactform/models.py:34 -msgid "contact form" -msgstr "Kontaktformular" - -#: content/contactform/models.py:35 -msgid "contact forms" -msgstr "Kontaktformulare" - -#: content/file/models.py:20 content/file/models.py:25 -#: module/medialibrary/models.py:84 -msgid "file" -msgstr "Datei" - -#: content/file/models.py:26 -msgid "files" -msgstr "Dateien" - -#: content/image/models.py:45 content/image/models.py:54 -msgid "image" -msgstr "Bild" - -#: content/image/models.py:48 -msgid "alternate text" -msgstr "Alternativtext" - -#: content/image/models.py:49 -msgid "Description of image" -msgstr "Bildlegende" - -#: content/image/models.py:50 module/medialibrary/models.py:231 -msgid "caption" -msgstr "Legende" - -#: content/image/models.py:55 -msgid "images" -msgstr "Bilder" - -#: content/image/models.py:80 -msgid "position" -msgstr "Position" - -#: content/image/models.py:88 -msgid "format" -msgstr "Format" - -#: content/medialibrary/models.py:49 content/section/models.py:36 -#: module/medialibrary/fields.py:59 module/medialibrary/models.py:97 +#: contents.py:50 module/medialibrary/fields.py:72 +#: module/medialibrary/models.py:112 msgid "media file" msgstr "Mediendatei" -#: content/medialibrary/models.py:50 module/medialibrary/models.py:98 +#: contents.py:51 module/medialibrary/models.py:113 msgid "media files" msgstr "Mediendateien" -#: content/medialibrary/models.py:58 content/section/models.py:52 -#: content/table/models.py:82 +#: contents.py:63 contents.py:268 msgid "type" msgstr "Typ" -#: content/raw/models.py:18 +#: contents.py:89 +msgid "content" +msgstr "Inhalt" + +#: contents.py:93 msgid "raw content" msgstr "Roh-Inhalt" -#: content/raw/models.py:19 +#: contents.py:94 msgid "raw contents" msgstr "Roh-Inhalte" -#: content/richtext/models.py:22 -msgid "HTML Tidy" -msgstr "HTML Tidy" - -#: content/richtext/models.py:23 -msgid "Ignore the HTML validation warnings" -msgstr "HTML-Validierungswarnungen ignorieren" - -#: content/richtext/models.py:47 -#, python-format -msgid "" -"HTML validation produced %(count)d warnings. Please review the updated " -"content below before continuing: %(messages)s" -msgstr "Die HTML-Validierung ergab %(count)d Warnungen. Bitte überprüfen Sie den aktualisierten Inhalt bevor Sie fortfahren: %(messages)s" - -#: content/richtext/models.py:84 content/section/models.py:35 +#: contents.py:120 msgid "text" msgstr "Text" -#: content/richtext/models.py:88 +#: contents.py:124 msgid "rich text" msgstr "Text" -#: content/richtext/models.py:89 +#: contents.py:125 msgid "rich texts" msgstr "Texte" -#: content/rss/models.py:21 -msgid "" -"The rss field is updated several times a day. A change in the title will " -"only be visible on the home page after the next feed update." -msgstr "Der RSS Feed wird mehrmals täglich aktualisiert. Eine Änderung des Titels erscheint erst nach der nächsten Feed-Aktualisierung auf der Webseite." - -#: content/rss/models.py:22 -msgid "link" -msgstr "Link" - -#: content/rss/models.py:23 -msgid "pre-rendered content" -msgstr "Vor-gerenderter Inhalt" - -#: content/rss/models.py:24 -msgid "last updated" -msgstr "Letzte Aktualisierung" - -#: content/rss/models.py:25 -msgid "max. items" -msgstr "Maximale Anzahl" - -#: content/rss/models.py:29 -msgid "RSS feed" -msgstr "RSS Feed" - -#: content/rss/models.py:30 -msgid "RSS feeds" -msgstr "RSS Feeds" - -#: content/section/models.py:41 -msgid "section" -msgstr "Sektion" - -#: content/section/models.py:42 -msgid "sections" -msgstr "Sektionen" - -#: content/table/models.py:63 -msgid "plain" -msgstr "schlicht" - -#: content/table/models.py:64 -msgid "title row" -msgstr "Titelzeile" - -#: content/table/models.py:66 -msgid "title row and column" -msgstr "Titelzeile und -spalte" - -#: content/table/models.py:72 -msgid "table" -msgstr "Tabelle" - -#: content/table/models.py:73 -msgid "tables" -msgstr "Tabellen" - -#: content/table/models.py:87 -msgid "data" -msgstr "Daten" - -#: content/template/models.py:51 +#: contents.py:166 msgid "template content" msgstr "Template" -#: content/template/models.py:52 +#: contents.py:167 msgid "template contents" msgstr "Templates" -#: content/video/models.py:31 -msgid "video link" -msgstr "Video-Link" +#: contents.py:172 models.py:396 +msgid "template" +msgstr "Template" -#: content/video/models.py:32 -msgid "" -"This should be a link to a youtube or vimeo video, i.e.: " -"http://www.youtube.com/watch?v=zmj1rpzDRZ0" -msgstr "Dies sollte ein Link zu einem Youtube- oder vimeo-Video sein, z.B.: http://www.youtube.com/watch?v=zmj1rpzDRZ0" +#: contents.py:214 contents.py:220 module/medialibrary/models.py:94 +msgid "file" +msgstr "Datei" -#: content/video/models.py:37 -msgid "video" -msgstr "Video" +#: contents.py:221 +msgid "files" +msgstr "Dateien" -#: content/video/models.py:38 -msgid "videos" -msgstr "Videos" +#: contents.py:250 contents.py:255 +msgid "image" +msgstr "Bild" -#: contrib/tagging.py:117 +#: contents.py:256 +msgid "images" +msgstr "Bilder" + +#: contrib/tagging.py:138 msgid "Tagging" msgstr "Tagging" -#: module/blog/models.py:28 -msgid "published" -msgstr "veröffentlicht" - -#: module/blog/models.py:30 -msgid "This is used for the generated navigation too." -msgstr "Dies wird auch für die generierte Navigation verwendet." - -#: module/blog/models.py:33 -msgid "published on" -msgstr "veröffentlicht am" - -#: module/blog/models.py:34 -msgid "" -"Will be set automatically once you tick the `published` checkbox above." -msgstr "Wird automatisch gesetzt, wenn `veröffentlicht` aktiviert ist." - -#: module/blog/models.py:39 -msgid "entry" -msgstr "Eintrag" - -#: module/blog/models.py:40 -msgid "entries" -msgstr "Einträge" - -#: module/blog/extensions/tags.py:12 -msgid "tags" -msgstr "Begriffe" - -#: module/blog/extensions/translations.py:20 -#: module/extensions/translations.py:126 -msgid "translation of" -msgstr "Übersetzung von" - -#: module/blog/extensions/translations.py:23 -#: module/extensions/translations.py:129 -msgid "Leave this empty for entries in the primary language." -msgstr "Dieses Feld für Einträge in der Primärsprache leer lassen." - -#: module/blog/extensions/translations.py:44 -#: templates/admin/feincms/item_editor.html:43 -msgid "available translations" -msgstr "Verfügbare Übersetzungen" - -#: module/extensions/changedate.py:32 +#: extensions/changedate.py:41 msgid "creation date" msgstr "Erstellt um" -#: module/extensions/changedate.py:33 +#: extensions/changedate.py:43 msgid "modification date" msgstr "Verändert um" -#: module/extensions/ct_tracker.py:140 +#: extensions/ct_tracker.py:154 msgid "content types" msgstr "Inhaltstypen" -#: module/extensions/datepublisher.py:67 +#: extensions/datepublisher.py:82 msgid "publication date" msgstr "Veröffentlichen am" -#: module/extensions/datepublisher.py:70 +#: extensions/datepublisher.py:86 msgid "publication end date" msgstr "Veröffentlicht bis" -#: module/extensions/datepublisher.py:72 +#: extensions/datepublisher.py:89 msgid "Leave empty if the entry should stay active forever." msgstr "Leer lassen wenn das Element ewig aktiv bleiben soll." -#: module/extensions/datepublisher.py:103 +#: extensions/datepublisher.py:125 msgid "visible from - to" msgstr "sichtbar von – bis" -#: module/extensions/datepublisher.py:113 +#: extensions/datepublisher.py:136 msgid "Date-based publishing" msgstr "Datumsbasierte Veröffentlichung" -#: module/extensions/featured.py:9 +#: extensions/featured.py:15 msgid "featured" msgstr "Feature" -#: module/extensions/featured.py:14 +#: extensions/featured.py:18 msgid "Featured" msgstr "Feature" -#: module/extensions/seo.py:9 +#: extensions/seo.py:16 msgid "meta keywords" msgstr "Meta Begriffe" -#: module/extensions/seo.py:10 -msgid "This will be prepended to the default keyword list." -msgstr "Diese Begriffe werden vor die Standard-Begriffsliste eingefügt." +#: extensions/seo.py:18 +msgid "Keywords are ignored by most search engines." +msgstr "Begriffe werden von den meisten Suchmaschinen ignoriert." -#: module/extensions/seo.py:11 +#: extensions/seo.py:20 msgid "meta description" msgstr "Meta Beschreibung" -#: module/extensions/seo.py:12 -msgid "This will be prepended to the default description." -msgstr "Diese Beschreibung wird vor der Standard-Beschreibung eingefügt." +#: extensions/seo.py:22 +msgid "" +"This text is displayed on the search results page. It is however not used " +"for the SEO ranking. Text longer than 140 characters is truncated." +msgstr "" +"Dieser Text wird auf der Suchresultatsseite angezeigt. Für das SEO Ranking " +"wird er nicht verwendet. Sollte nicht länger als 140 Zeichen sein." -#: module/extensions/seo.py:17 +#: extensions/seo.py:32 msgid "Search engine optimization" msgstr "Suchmaschinenoptimierung" -#: module/extensions/translations.py:207 +#: extensions/translations.py:154 translations.py:282 +msgid "language" +msgstr "Sprache" + +#: extensions/translations.py:162 +msgid "translation of" +msgstr "Übersetzung von" + +#: extensions/translations.py:166 +msgid "Leave this empty for entries in the primary language." +msgstr "Dieses Feld für Einträge in der Primärsprache leer lassen." + +#: extensions/translations.py:267 msgid "Edit translation" msgstr "Übersetzungen bearbeiten" -#: module/extensions/translations.py:210 +#: extensions/translations.py:274 msgid "Create translation" msgstr "Übersetzung erstellen" -#: module/extensions/translations.py:215 +#: extensions/translations.py:282 msgid "translations" msgstr "Übersetzungen" -#: module/medialibrary/forms.py:26 +#: models.py:511 +msgid "ordering" +msgstr "Sortierung" + +#: module/medialibrary/forms.py:31 msgid "This would create a loop in the hierarchy" msgstr "Dies würde eine Endlosschleife in der Hierarchie erzeugen." -#: module/medialibrary/forms.py:64 +#: module/medialibrary/forms.py:77 #, python-format msgid "" "Cannot overwrite with different file type (attempt to overwrite a " "%(old_ext)s with a %(new_ext)s)" -msgstr "Dateien verschiedenen Typs können nicht überschrieben werden. (%(old_ext)s durch %(new_ext)s)." +msgstr "" +"Dateien verschiedenen Typs können nicht überschrieben werden. (%(old_ext)s " +"durch %(new_ext)s)." -#: module/medialibrary/modeladmins.py:58 +#: module/medialibrary/modeladmins.py:63 #, python-format msgid "Successfully added %(count)d media file to %(category)s." msgid_plural "Successfully added %(count)d media files to %(category)s." msgstr[0] "Erfolgreich %(count)d Mediendatei zu %(category)s hinzugefügt." msgstr[1] "Erfolgreich %(count)d Mediendateien zu %(category)s hinzugefügt." -#: module/medialibrary/modeladmins.py:77 +#: module/medialibrary/modeladmins.py:84 msgid "Add selected media files to category" msgstr "Ausgewählte Mediendateien zu Kategorie hinzufügen" -#: module/medialibrary/modeladmins.py:86 +#: module/medialibrary/modeladmins.py:94 #, python-format msgid "ZIP file exported as %s" msgstr "ZIP-Datei als %s exportiert." -#: module/medialibrary/modeladmins.py:88 +#: module/medialibrary/modeladmins.py:96 #, python-format msgid "ZIP file export failed: %s" msgstr "ZIP Export fehlgeschlagen: %s" -#: module/medialibrary/modeladmins.py:93 +#: module/medialibrary/modeladmins.py:104 msgid "Export selected media files as zip file" msgstr "Ausgewählte Mediendateien als ZIP-Archiv exportieren." -#: module/medialibrary/modeladmins.py:136 -#: templates/admin/feincms/page/page/item_editor.html:15 +#: module/medialibrary/modeladmins.py:154 +#: templates/admin/feincms/page/page/item_editor.html:11 msgid "Preview" msgstr "Vorschau" -#: module/medialibrary/modeladmins.py:141 module/medialibrary/models.py:88 +#: module/medialibrary/modeladmins.py:159 module/medialibrary/models.py:103 msgid "file size" msgstr "Dateigrösse" -#: module/medialibrary/modeladmins.py:146 module/medialibrary/models.py:86 +#: module/medialibrary/modeladmins.py:164 module/medialibrary/models.py:100 msgid "created" msgstr "Erstellt" -#: module/medialibrary/modeladmins.py:165 module/medialibrary/models.py:85 +#: module/medialibrary/modeladmins.py:184 module/medialibrary/models.py:97 msgid "file type" msgstr "Dateityp" -#: module/medialibrary/modeladmins.py:186 +#: module/medialibrary/modeladmins.py:208 msgid "file info" msgstr "Dateiinfo" -#: module/medialibrary/modeladmins.py:198 +#: module/medialibrary/modeladmins.py:221 #, python-format msgid "%d files imported" msgstr "%d Dateien importiert" -#: module/medialibrary/modeladmins.py:200 +#: module/medialibrary/modeladmins.py:223 #, python-format msgid "ZIP import failed: %s" msgstr "ZIP Import fehlgeschlagen: %s" -#: module/medialibrary/modeladmins.py:202 +#: module/medialibrary/modeladmins.py:225 msgid "No input file given" msgstr " Keine Datei angegeben" -#: module/medialibrary/models.py:43 +#: module/medialibrary/models.py:49 msgid "parent" msgstr "Übergeordnet" -#: module/medialibrary/models.py:45 module/page/models.py:171 +#: module/medialibrary/models.py:51 module/page/models.py:162 msgid "slug" msgstr "Slug" -#: module/medialibrary/models.py:49 +#: module/medialibrary/models.py:55 msgid "category" msgstr "Kategorie" -#: module/medialibrary/models.py:50 module/medialibrary/models.py:90 +#: module/medialibrary/models.py:56 module/medialibrary/models.py:106 msgid "categories" msgstr "Kategorien" -#: module/medialibrary/models.py:87 +#: module/medialibrary/models.py:101 msgid "copyright" msgstr "Copyright" -#: module/medialibrary/models.py:202 +#: module/medialibrary/models.py:219 msgid "Image" msgstr "Bild" -#: module/medialibrary/models.py:203 +#: module/medialibrary/models.py:221 msgid "Video" msgstr "Video" -#: module/medialibrary/models.py:204 +#: module/medialibrary/models.py:224 msgid "Audio" msgstr "Audio" -#: module/medialibrary/models.py:205 +#: module/medialibrary/models.py:226 msgid "PDF document" msgstr "PDF-Dokument" -#: module/medialibrary/models.py:206 +#: module/medialibrary/models.py:227 msgid "Flash" msgstr "Flash" -#: module/medialibrary/models.py:207 +#: module/medialibrary/models.py:228 msgid "Text" msgstr "Text" -#: module/medialibrary/models.py:208 +#: module/medialibrary/models.py:229 msgid "Rich Text" msgstr "Text" -#: module/medialibrary/models.py:209 +#: module/medialibrary/models.py:230 msgid "Zip archive" msgstr "ZIP-Archiv" -#: module/medialibrary/models.py:210 +#: module/medialibrary/models.py:231 msgid "Microsoft Word" msgstr "Microsoft Word" -#: module/medialibrary/models.py:211 +#: module/medialibrary/models.py:233 msgid "Microsoft Excel" msgstr "Microsoft Excel" -#: module/medialibrary/models.py:212 +#: module/medialibrary/models.py:235 msgid "Microsoft PowerPoint" msgstr "Microsoft PowerPoint" -#: module/medialibrary/models.py:213 +#: module/medialibrary/models.py:237 msgid "Binary" msgstr "Binärdaten" -#: module/medialibrary/models.py:232 +#: module/medialibrary/models.py:260 +msgid "caption" +msgstr "Legende" + +#: module/medialibrary/models.py:261 msgid "description" msgstr "Beschreibung" -#: module/medialibrary/models.py:235 +#: module/medialibrary/models.py:264 msgid "media file translation" msgstr "Mediendatei-Übersetzung" -#: module/medialibrary/models.py:236 +#: module/medialibrary/models.py:265 msgid "media file translations" msgstr "Mediendatei-Übersetzungen" -#: module/page/forms.py:172 +#: module/page/extensions/excerpt.py:18 +msgid "excerpt" +msgstr "Auszug" + +#: module/page/extensions/excerpt.py:21 +msgid "Add a brief excerpt summarizing the content of this page." +msgstr "Kurze Zusammenfassung des Seiteninhaltes." + +#: module/page/extensions/excerpt.py:25 +msgid "Excerpt" +msgstr "Auszug" + +#: module/page/extensions/navigation.py:90 +#: module/page/extensions/navigation.py:152 +msgid "navigation extension" +msgstr "Navigations-Erweiterung" + +#: module/page/extensions/navigation.py:156 +msgid "" +"Select the module providing subpages for this page if you need to customize " +"the navigation." +msgstr "Wähle das Modul aus, welches weitere Navigationspunkte erstellt." + +#: module/page/extensions/navigation.py:179 +msgid "Navigation extension" +msgstr "Navigations-Erweiterung" + +#: module/page/extensions/navigationgroups.py:17 +msgid "Default" +msgstr "Standard" + +#: module/page/extensions/navigationgroups.py:18 +msgid "Footer" +msgstr "Fusszeile" + +#: module/page/extensions/navigationgroups.py:25 +msgid "navigation group" +msgstr "Navigationsgruppe" + +#: module/page/extensions/relatedpages.py:20 +msgid "Select pages that should be listed as related content." +msgstr "Seiten auswählen, welche als ähnlicher Inhalt angezeigt werden sollen." + +#: module/page/extensions/relatedpages.py:25 +msgid "Related pages" +msgstr "Verwandte Seiten" + +#: module/page/extensions/sites.py:21 +msgid "Site" +msgstr "Seite" + +#: module/page/extensions/symlinks.py:22 +msgid "symlinked page" +msgstr "Verbundene Seite" + +#: module/page/extensions/symlinks.py:23 +msgid "All content is inherited from this page if given." +msgstr "" +"Der angezeigte Inhalt wird durch den Inhalt der angegebenen Seite ersetzt." + +#: module/page/extensions/titles.py:19 +msgid "content title" +msgstr "Inhaltstitel" + +#: module/page/extensions/titles.py:22 +msgid "The first line is the main title, the following lines are subtitles." +msgstr "Die erste Zeile ist der Haupttitel, die weiteren Zeilen Untertitel" + +#: module/page/extensions/titles.py:26 +msgid "page title" +msgstr "Seitentitel" + +#: module/page/extensions/titles.py:30 +#| msgid "" +#| "Page title for browser window. Same as title bydefault. Must not be " +#| "longer than 70 characters." +msgid "" +"Page title for browser window. Same as title by default. Must be 69 " +"characters or fewer." +msgstr "" +"Seitentitel für das Browser-Fenster. Standardmässig gleich wie der Titel." +"Darf nicht länger als 69 Zeichen sein." + +#: module/page/extensions/titles.py:60 +msgid "Titles" +msgstr "Titel" + +#: module/page/forms.py:185 msgid "This URL is already taken by an active page." msgstr "Die URL wird schon von einer aktiven Seite verwendet." -#: module/page/forms.py:190 +#: module/page/forms.py:204 msgid "This URL is already taken by another active page." msgstr "Die URL wird schon von einer anderen aktiven Seite verwendet." -#: module/page/modeladmins.py:41 +#: module/page/forms.py:209 +msgid "This page does not allow attachment of child pages" +msgstr "Diese Seite erlaubt keine Unterseiten" + +#: module/page/modeladmins.py:42 msgid "Other options" msgstr "Weitere Optionen" -#: module/page/modeladmins.py:90 module/page/models.py:174 +#: module/page/modeladmins.py:80 module/page/models.py:169 msgid "in navigation" msgstr "Im Menü" -#: module/page/modeladmins.py:105 +#: module/page/modeladmins.py:109 module/page/modeladmins.py:111 msgid "Add child page" msgstr "Unterseite hinzufügen" -#: module/page/modeladmins.py:107 +#: module/page/modeladmins.py:120 module/page/modeladmins.py:122 #: templates/admin/feincms/content_inline.html:9 msgid "View on site" msgstr "Auf der Webseite anzeigen" -#: module/page/modeladmins.py:126 +#: module/page/modeladmins.py:142 #, python-format -msgid "Add %(language)s Translation of \"%(page)s\"" -msgstr "" +msgid "Add %(language)s translation of \"%(page)s\"" +msgstr "%(language)s-Übersetzung für \"%(page)s\" hinzufügen" -#: module/page/modeladmins.py:156 +#: module/page/modeladmins.py:177 msgid "" "The content from the original translation has been copied to the newly " "created page." msgstr "Der Inhalt der Originalseite wurde auf die neue Seite kopiert." -#: module/page/modeladmins.py:170 +#: module/page/modeladmins.py:197 msgid "You don't have the necessary permissions to edit this object" -msgstr "Sie haben die nötige Berechtigung nicht, um dieses Objekt zu bearbeiten" +msgstr "" +"Sie haben die nötige Berechtigung nicht, um dieses Objekt zu bearbeiten" -#: module/page/modeladmins.py:185 +#: module/page/modeladmins.py:223 msgid "inherited" msgstr "geerbt" -#: module/page/modeladmins.py:189 +#: module/page/modeladmins.py:229 msgid "extensions" msgstr "Erweiterungen" -#: module/page/modeladmins.py:193 module/page/models.py:206 +#: module/page/modeladmins.py:233 module/page/models.py:212 msgid "is active" msgstr "Aktiv" -#: module/page/models.py:167 +#: module/page/models.py:156 msgid "active" msgstr "Aktiv" -#: module/page/models.py:175 +#: module/page/models.py:160 +msgid "This title is also used for navigation menu items." +msgstr "Dieser Titel wird auch für die generierte Navigation verwendet." + +#: module/page/models.py:163 +msgid "This is used to build the URL for this page" +msgstr "Dies wird verwendet um die URL der Seite zu generieren" + +#: module/page/models.py:171 msgid "override URL" msgstr "Überschriebene URL" -#: module/page/models.py:176 +#: module/page/models.py:173 msgid "" "Override the target URL. Be sure to include slashes at the beginning and at " -"the end if it is a local URL. This affects both the navigation and subpages'" -" URLs." -msgstr "Überschreibe die URL. Am Anfang und Ende muss ein / stehen, falls es sich um eine lokale URL handelt. Dieses Feld bestimmt die Navigation und die URLs von Unterseiten." +"the end if it is a local URL. This affects both the navigation and subpages' " +"URLs." +msgstr "" +"Überschreibe die URL. Am Anfang und Ende muss ein / stehen, falls es sich um " +"eine lokale URL handelt. Dieses Feld bestimmt die Navigation und die URLs " +"von Unterseiten." #: module/page/models.py:177 msgid "redirect to" msgstr "Weiterleiten zu" -#: module/page/models.py:178 +#: module/page/models.py:180 msgid "Target URL for automatic redirects or the primary key of a page." msgstr "" +"Adresse für automatische Weiterleitungen, oder Primärschlüssel einer Seite" -#: module/page/models.py:180 +#: module/page/models.py:183 msgid "Cached URL" msgstr "Zwischengespeicherte URL" -#: module/page/models.py:395 +#: module/page/models.py:285 +#, python-format +msgid "" +"This %(page_class)s uses a singleton template, and " +"FEINCMS_SINGLETON_TEMPLATE_DELETION_ALLOWED=False" +msgstr "" +"Diese %(page_class)s verwendet ein Singleton Template, und " +"FEINCMS_SINGLETON_TEMPLATE_DELETION_ALLOWED=False" + +#: module/page/models.py:376 msgid "page" msgstr "Seite" -#: module/page/models.py:396 +#: module/page/models.py:377 msgid "pages" msgstr "Seiten" -#: module/page/extensions/excerpt.py:9 -msgid "excerpt" -msgstr "Auszug" - -#: module/page/extensions/excerpt.py:10 -msgid "Add a brief excerpt summarizing the content of this page." -msgstr "Kurze Zusammenfassung des Seiteninhaltes." - -#: module/page/extensions/excerpt.py:12 -msgid "Excerpt" -msgstr "Auszug" - -#: module/page/extensions/navigation.py:82 -#: module/page/extensions/navigation.py:107 -msgid "navigation extension" -msgstr "Navigations-Erweiterung" - -#: module/page/extensions/navigation.py:110 -msgid "" -"Select the module providing subpages for this page if you need to customize " -"the navigation." -msgstr "Wähle das Modul aus, welches weitere Navigationspunkte erstellt." - -#: module/page/extensions/navigation.py:125 -msgid "Navigation extension" -msgstr "Navigations-Erweiterung" - -#: module/page/extensions/relatedpages.py:14 -msgid "Select pages that should be listed as related content." -msgstr "Seiten auswählen, welche als ähnlicher Inhalt angezeigt werden sollen." - -#: module/page/extensions/relatedpages.py:19 -msgid "Related pages" -msgstr "Verwandte Seiten" - -#: module/page/extensions/sites.py:16 -msgid "Site" -msgstr "Seite" - -#: module/page/extensions/symlinks.py:15 -msgid "symlinked page" -msgstr "Verbundene Seite" - -#: module/page/extensions/symlinks.py:16 -msgid "All content is inherited from this page if given." -msgstr "Der angezeigte Inhalt wird durch den Inhalt der angegebenen Seite ersetzt." - -#: module/page/extensions/titles.py:13 -msgid "content title" -msgstr "Inhaltstitel" - -#: module/page/extensions/titles.py:14 -msgid "The first line is the main title, the following lines are subtitles." -msgstr "Die erste Zeile ist der Haupttitel, die weiteren Zeilen Untertitel" - -#: module/page/extensions/titles.py:15 -msgid "page title" -msgstr "Seitentitel" - -#: module/page/extensions/titles.py:16 -msgid "Page title for browser window. Same as title by default." -msgstr "Seitentitel für das Browser-Fenster. Standardmässig gleich wie der Titel." - -#: module/page/extensions/titles.py:43 -msgid "Titles" -msgstr "Titel" - -#: templates/admin/filter.html:3 -#, python-format -msgid " By %(filter_title)s " -msgstr " Nach %(filter_title)s " - #: templates/admin/feincms/_messages_js.html:4 msgid "Really delete item?" msgstr "Element wirklich löschen?" @@ -771,10 +632,13 @@ msgstr "Template wirklich ändern?
    Alle Änderungen werden gespeichert." #: templates/admin/feincms/_messages_js.html:9 #, python-format msgid "" -"Really change template?
    All changes are saved and content from " -"%%(source_regions)s is moved to " -"%%(target_region)s." -msgstr "Template wechseln?
    Alle Änderungen werden sofort gespeichert und Inhalt von \n%%(source_region)s nach %%(target_region)s verschoben." +"Really change template?
    All changes are saved and content from " +"%%(source_regions)s is moved to %%(target_region)s." +msgstr "" +"Template wechseln?
    Alle Änderungen werden sofort gespeichert und " +"Inhalt von " +"%%(source_region)s nach %%(target_region)s " +"verschoben." #: templates/admin/feincms/_messages_js.html:12 msgid "Hide" @@ -796,9 +660,18 @@ msgstr "Vorher" msgid "Insert new:" msgstr "Neu einfügen:" +#: templates/admin/feincms/_messages_js.html:17 +msgid "Move to region:" +msgstr "Nach Region verschieben:" + +#: templates/admin/feincms/_messages_js.html:18 +#| msgid "Insert new:" +msgid "Insert new content:" +msgstr "Neuen Inhalt einfügen:" + #: templates/admin/feincms/content_editor.html:15 msgid "Copy content from the original" -msgstr "" +msgstr "Inhalt von Original übernehmen" #: templates/admin/feincms/content_editor.html:19 msgid "Region empty" @@ -808,101 +681,80 @@ msgstr "Region leer" msgid "" "Content from the parent site is automatically inherited. To override this " "behaviour, add some content." -msgstr "Inhalt wird von der übergeordneten Seite geerbt. Füge Inhalt hinzu, um dieses Verhalten zu ändern" +msgstr "" +"Inhalt wird von der übergeordneten Seite geerbt. Füge Inhalt hinzu, um " +"dieses Verhalten zu ändern" #: templates/admin/feincms/content_editor.html:33 msgid "Add new item" msgstr "Neues Element hinzufügen" -#: templates/admin/feincms/content_inline.html:91 +#: templates/admin/feincms/content_inline.html:93 #, python-format msgid "Add another %(verbose_name)s" msgstr "Weiteres %(verbose_name)s hinzufügen" -#: templates/admin/feincms/content_inline.html:94 +#: templates/admin/feincms/content_inline.html:96 msgid "Remove" msgstr "Entfernen" -#: templates/admin/feincms/fe_editor.html:40 -msgid "Save" -msgstr "Sichern" - -#: templates/admin/feincms/fe_tools.html:28 -msgid "Stop Editing" -msgstr "Fertig bearbeitet" - -#: templates/admin/feincms/fe_tools.html:33 -msgid "edit" -msgstr "bearbeiten" - -#: templates/admin/feincms/fe_tools.html:35 -msgid "new" -msgstr "neu" - -#: templates/admin/feincms/fe_tools.html:36 -msgid "up" -msgstr "rauf" - -#: templates/admin/feincms/fe_tools.html:37 -msgid "down" -msgstr "runter" - -#: templates/admin/feincms/fe_tools.html:38 -msgid "remove" -msgstr "entfernen" +#: templates/admin/feincms/item_editor.html:33 +msgid "available translations" +msgstr "Verfügbare Übersetzungen" -#: templates/admin/feincms/recover_form.html:8 -#: templates/admin/feincms/revision_form.html:8 -#: templates/admin/feincms/page/page/item_editor.html:23 +#: templates/admin/feincms/page/page/item_editor.html:19 #: templates/admin/feincms/page/page/tree_editor.html:7 +#: templates/admin/feincms/recover_form.html:7 +#: templates/admin/feincms/revision_form.html:7 msgid "Home" msgstr "Startseite" -#: templates/admin/feincms/recover_form.html:11 +#: templates/admin/feincms/page/page/item_editor.html:23 +msgid "Add" +msgstr "Hinzufügen" + +#: templates/admin/feincms/recover_form.html:10 #, python-format msgid "Recover deleted %(verbose_name)s" msgstr "%(verbose_name)s wiederherstellen" -#: templates/admin/feincms/recover_form.html:17 +#: templates/admin/feincms/recover_form.html:16 msgid "Press the save button below to recover this version of the object." msgstr "Speichern drücken, um diese Version dieses Objekts wiederherzustellen" -#: templates/admin/feincms/revision_form.html:12 +#: templates/admin/feincms/revision_form.html:11 msgid "History" msgstr "Verlauf" -#: templates/admin/feincms/revision_form.html:13 +#: templates/admin/feincms/revision_form.html:12 #, python-format msgid "Revert %(verbose_name)s" msgstr "%(verbose_name)s zurücksetzen" -#: templates/admin/feincms/revision_form.html:24 +#: templates/admin/feincms/revision_form.html:23 msgid "Press the save button below to revert to this version of the object." msgstr "Speichern drücken, um zu dieser Version des Objektes zurückzukehren." -#: templates/admin/feincms/tree_editor.html:32 +#: templates/admin/feincms/tree_editor.html:22 msgid "Shortcuts" msgstr "Schnellzugriffe" -#: templates/admin/feincms/tree_editor.html:34 +#: templates/admin/feincms/tree_editor.html:24 msgid "Collapse tree" msgstr "Alles zuklappen" -#: templates/admin/feincms/tree_editor.html:35 +#: templates/admin/feincms/tree_editor.html:25 msgid "Expand tree" msgstr "Alles aufklappen" -#: templates/admin/feincms/tree_editor.html:38 +#: templates/admin/feincms/tree_editor.html:29 msgid "Filter" msgstr "Filter" -#: templates/admin/feincms/page/page/item_editor.html:10 -msgid "Edit on site" -msgstr "In der Seite bearbeiten" - -#: templates/admin/feincms/page/page/item_editor.html:27 -msgid "Add" -msgstr "Hinzufügen" +#: templates/admin/filter.html:3 +#, python-format +msgid " By %(filter_title)s " +msgstr " Nach %(filter_title)s " #: templates/admin/medialibrary/add_to_category.html:5 #: templates/admin/medialibrary/add_to_category.html:9 @@ -915,7 +767,8 @@ msgstr "Kategorie auswählen:" #: templates/admin/medialibrary/add_to_category.html:17 msgid "The following media files will be added to the selected category:" -msgstr "Die folgenden Mediendateien werdene zur ausgewählten Kategorie hinzugefügt:" +msgstr "" +"Die folgenden Mediendateien werdene zur ausgewählten Kategorie hinzugefügt:" #: templates/admin/medialibrary/add_to_category.html:22 msgid "Add to category" @@ -925,39 +778,18 @@ msgstr "Zu Kategorie hinzufügen" msgid "Cancel" msgstr "Abbrechen" -#: templates/admin/medialibrary/mediafile/change_list.html:11 +#: templates/admin/medialibrary/mediafile/change_list.html:10 msgid "Bulk upload a ZIP file:" msgstr "Massenupload mit ZIP Dateien:" -#: templates/admin/medialibrary/mediafile/change_list.html:21 +#: templates/admin/medialibrary/mediafile/change_list.html:20 msgid "Overwrite" msgstr "Überschreiben" -#: templates/admin/medialibrary/mediafile/change_list.html:24 +#: templates/admin/medialibrary/mediafile/change_list.html:23 msgid "Send" msgstr "Senden" -#: templates/content/comments/default.html:10 -#, python-format -msgid "%(comment_count)s comments." -msgstr "%(comment_count)s Kommentare." - -#: templates/content/comments/default.html:18 -#, python-format -msgid "" -"\n" -" %(comment_username)s said on %(comment_submit_date)s
    \n" -" " -msgstr "\n%(comment_username)s schrieb am %(comment_submit_date)s
    " - -#: templates/content/comments/default.html:28 -msgid "No comments." -msgstr "Keine Kommentare." - -#: templates/content/comments/default.html:36 -msgid "Post Comment" -msgstr "Kommentar abschicken" - #: templates/content/contactform/form.html:9 msgid "Submit" msgstr "Senden" @@ -965,3 +797,196 @@ msgstr "Senden" #: templates/content/contactform/thanks.html:3 msgid "Thanks!" msgstr "Danke!" + +#~ msgid "Change %s" +#~ msgstr "%s ändern" + +#~ msgid "enabled" +#~ msgstr "aktiviert" + +#~ msgid "New comments may be added" +#~ msgstr "Neue Kommentare können hinzugefügt werden" + +#~ msgid "comments" +#~ msgstr "Kommentare" + +#~ msgid "public" +#~ msgstr "veröffentlicht" + +#~ msgid "not public" +#~ msgstr "nicht öffentlich" + +#~ msgid "name" +#~ msgstr "Name" + +#~ msgid "email" +#~ msgstr "E-Mail" + +#~ msgid "subject" +#~ msgstr "Betreff" + +#~ msgid "contact form" +#~ msgstr "Kontaktformular" + +#~ msgid "contact forms" +#~ msgstr "Kontaktformulare" + +#~ msgid "alternate text" +#~ msgstr "Alternativtext" + +#~ msgid "Description of image" +#~ msgstr "Bildlegende" + +#~ msgid "position" +#~ msgstr "Position" + +#~ msgid "format" +#~ msgstr "Format" + +#~ msgid "HTML Tidy" +#~ msgstr "HTML Tidy" + +#~ msgid "Ignore the HTML validation warnings" +#~ msgstr "HTML-Validierungswarnungen ignorieren" + +#~ msgid "" +#~ "HTML validation produced %(count)d warnings. Please review the updated " +#~ "content below before continuing: %(messages)s" +#~ msgstr "" +#~ "Die HTML-Validierung ergab %(count)d Warnungen. Bitte überprüfen Sie den " +#~ "aktualisierten Inhalt bevor Sie fortfahren: %(messages)s" + +#~ msgid "" +#~ "The rss field is updated several times a day. A change in the title will " +#~ "only be visible on the home page after the next feed update." +#~ msgstr "" +#~ "Der RSS Feed wird mehrmals täglich aktualisiert. Eine Änderung des Titels " +#~ "erscheint erst nach der nächsten Feed-Aktualisierung auf der Webseite." + +#~ msgid "link" +#~ msgstr "Link" + +#~ msgid "pre-rendered content" +#~ msgstr "Vor-gerenderter Inhalt" + +#~ msgid "last updated" +#~ msgstr "Letzte Aktualisierung" + +#~ msgid "max. items" +#~ msgstr "Maximale Anzahl" + +#~ msgid "RSS feed" +#~ msgstr "RSS Feed" + +#~ msgid "RSS feeds" +#~ msgstr "RSS Feeds" + +#~ msgid "section" +#~ msgstr "Sektion" + +#~ msgid "sections" +#~ msgstr "Sektionen" + +#~ msgid "video link" +#~ msgstr "Video-Link" + +#~ msgid "" +#~ "This should be a link to a youtube or vimeo video, i.e.: http://www." +#~ "youtube.com/watch?v=zmj1rpzDRZ0" +#~ msgstr "" +#~ "Dies sollte ein Link zu einem Youtube- oder vimeo-Video sein, z.B.: " +#~ "http://www.youtube.com/watch?v=zmj1rpzDRZ0" + +#~ msgid "video" +#~ msgstr "Video" + +#~ msgid "videos" +#~ msgstr "Videos" + +#~ msgid "tags" +#~ msgstr "Begriffe" + +#~ msgid "published" +#~ msgstr "veröffentlicht" + +#~ msgid "This is used for the generated navigation too." +#~ msgstr "Dies wird auch für die generierte Navigation verwendet." + +#~ msgid "published on" +#~ msgstr "veröffentlicht am" + +#~ msgid "" +#~ "Will be set automatically once you tick the `published` checkbox above." +#~ msgstr "Wird automatisch gesetzt, wenn `veröffentlicht` aktiviert ist." + +#~ msgid "entry" +#~ msgstr "Eintrag" + +#~ msgid "entries" +#~ msgstr "Einträge" + +#~ msgid "Save" +#~ msgstr "Sichern" + +#~ msgid "Stop Editing" +#~ msgstr "Fertig bearbeitet" + +#~ msgid "edit" +#~ msgstr "bearbeiten" + +#~ msgid "new" +#~ msgstr "neu" + +#~ msgid "up" +#~ msgstr "rauf" + +#~ msgid "down" +#~ msgstr "runter" + +#~ msgid "remove" +#~ msgstr "entfernen" + +#~ msgid "Edit on site" +#~ msgstr "In der Seite bearbeiten" + +#~ msgid "%(comment_count)s comments." +#~ msgstr "%(comment_count)s Kommentare." + +#~ msgid "" +#~ "\n" +#~ " %(comment_username)s said on " +#~ "%(comment_submit_date)s
    \n" +#~ " " +#~ msgstr "" +#~ "\n" +#~ "%(comment_username)s schrieb am %(comment_submit_date)s
    " + +#~ msgid "No comments." +#~ msgstr "Keine Kommentare." + +#~ msgid "Post Comment" +#~ msgstr "Kommentar abschicken" + +#~ msgid "plain" +#~ msgstr "schlicht" + +#~ msgid "title row" +#~ msgstr "Titelzeile" + +#~ msgid "title row and column" +#~ msgstr "Titelzeile und -spalte" + +#~ msgid "table" +#~ msgstr "Tabelle" + +#~ msgid "tables" +#~ msgstr "Tabellen" + +#~ msgid "data" +#~ msgstr "Daten" + +#~ msgid "This will be prepended to the default keyword list." +#~ msgstr "Diese Begriffe werden vor die Standard-Begriffsliste eingefügt." + +#~ msgid "This will be prepended to the default description." +#~ msgstr "Diese Beschreibung wird vor der Standard-Beschreibung eingefügt." diff --git a/feincms/locale/en/LC_MESSAGES/django.po b/feincms/locale/en/LC_MESSAGES/django.po index c7f2fdc33..da4414571 100644 --- a/feincms/locale/en/LC_MESSAGES/django.po +++ b/feincms/locale/en/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2013-10-25 11:12+0200\n" +"POT-Creation-Date: 2014-07-20 14:59+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -17,724 +17,737 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: models.py:370 content/template/models.py:59 -msgid "template" -msgstr "" - -#: models.py:510 -msgid "ordering" -msgstr "" - -#: translations.py:259 module/blog/extensions/translations.py:17 -#: module/extensions/translations.py:123 -msgid "language" -msgstr "" - -#: admin/filterspecs.py:35 admin/filterspecs.py:70 +#: admin/filterspecs.py:47 admin/filterspecs.py:86 msgid "All" msgstr "" -#: admin/filterspecs.py:46 module/page/models.py:172 +#: admin/filterspecs.py:58 module/page/models.py:178 msgid "Parent" msgstr "" -#: admin/filterspecs.py:81 +#: admin/filterspecs.py:97 #: templates/admin/medialibrary/mediafile/change_list.html:14 msgid "Category" msgstr "" -#: admin/item_editor.py:159 +#: admin/item_editor.py:190 #, python-format msgid "Change %s" msgstr "" -#: admin/tree_editor.py:234 content/rss/models.py:20 -#: content/section/models.py:34 module/blog/models.py:29 -#: module/medialibrary/models.py:40 module/page/models.py:170 -#: module/page/models.py:226 +#: admin/tree_editor.py:294 content/rss/models.py:23 +#: content/section/models.py:35 module/blog/models.py:35 +#: module/medialibrary/models.py:45 module/page/models.py:172 +#: module/page/models.py:240 msgid "title" msgstr "" -#: admin/tree_editor.py:407 +#: admin/tree_editor.py:353 admin/tree_editor.py:370 +msgid "You do not have permission to modify this object" +msgstr "" + +#: admin/tree_editor.py:491 +msgid "No permission" +msgstr "" + +#: admin/tree_editor.py:509 #, python-format msgid "%s has been moved to a new position." msgstr "" -#: admin/tree_editor.py:411 +#: admin/tree_editor.py:512 msgid "Did not understand moving instruction." msgstr "" -#: admin/tree_editor.py:420 +#: admin/tree_editor.py:523 msgid "actions" msgstr "" -#: admin/tree_editor.py:432 +#: admin/tree_editor.py:552 #, python-format -msgid "Successfully deleted %s items." +msgid "Successfully deleted %(count)d items." msgstr "" -#: admin/tree_editor.py:440 +#: admin/tree_editor.py:565 #, python-format msgid "Delete selected %(verbose_name_plural)s" msgstr "" -#: content/application/models.py:218 +#: content/application/models.py:147 msgid "application content" msgstr "" -#: content/application/models.py:219 +#: content/application/models.py:148 msgid "application contents" msgstr "" -#: content/application/models.py:249 +#: content/application/models.py:179 msgid "application" msgstr "" -#: content/comments/models.py:24 +#: content/comments/models.py:32 msgid "enabled" msgstr "" -#: content/comments/models.py:24 +#: content/comments/models.py:33 msgid "New comments may be added" msgstr "" -#: content/comments/models.py:28 content/comments/models.py:29 +#: content/comments/models.py:37 content/comments/models.py:38 msgid "comments" msgstr "" -#: content/comments/models.py:49 +#: content/comments/models.py:60 msgid "public" msgstr "" -#: content/comments/models.py:49 +#: content/comments/models.py:61 msgid "not public" msgstr "" -#: content/contactform/models.py:18 +#: content/contactform/models.py:20 msgid "name" msgstr "" -#: content/contactform/models.py:19 +#: content/contactform/models.py:21 msgid "email" msgstr "" -#: content/contactform/models.py:20 +#: content/contactform/models.py:22 msgid "subject" msgstr "" -#: content/contactform/models.py:23 content/raw/models.py:14 +#: content/contactform/models.py:26 content/raw/models.py:16 msgid "content" msgstr "" -#: content/contactform/models.py:34 +#: content/contactform/models.py:37 msgid "contact form" msgstr "" -#: content/contactform/models.py:35 +#: content/contactform/models.py:38 msgid "contact forms" msgstr "" -#: content/file/models.py:20 content/file/models.py:25 -#: module/medialibrary/models.py:84 +#: content/file/models.py:23 content/file/models.py:28 +#: module/medialibrary/models.py:94 msgid "file" msgstr "" -#: content/file/models.py:26 +#: content/file/models.py:29 msgid "files" msgstr "" -#: content/image/models.py:45 content/image/models.py:54 +#: content/image/models.py:46 content/image/models.py:55 msgid "image" msgstr "" -#: content/image/models.py:48 +#: content/image/models.py:49 msgid "alternate text" msgstr "" -#: content/image/models.py:49 +#: content/image/models.py:50 msgid "Description of image" msgstr "" -#: content/image/models.py:50 module/medialibrary/models.py:231 +#: content/image/models.py:51 module/medialibrary/models.py:260 msgid "caption" msgstr "" -#: content/image/models.py:55 +#: content/image/models.py:56 msgid "images" msgstr "" -#: content/image/models.py:80 +#: content/image/models.py:82 msgid "position" msgstr "" -#: content/image/models.py:88 +#: content/image/models.py:90 msgid "format" msgstr "" -#: content/medialibrary/models.py:49 content/section/models.py:36 -#: module/medialibrary/fields.py:59 module/medialibrary/models.py:97 +#: content/medialibrary/models.py:51 content/section/models.py:38 +#: module/medialibrary/fields.py:66 module/medialibrary/models.py:112 msgid "media file" msgstr "" -#: content/medialibrary/models.py:50 module/medialibrary/models.py:98 +#: content/medialibrary/models.py:52 module/medialibrary/models.py:113 msgid "media files" msgstr "" -#: content/medialibrary/models.py:58 content/section/models.py:52 -#: content/table/models.py:82 +#: content/medialibrary/models.py:64 content/section/models.py:59 msgid "type" msgstr "" -#: content/raw/models.py:18 +#: content/raw/models.py:20 msgid "raw content" msgstr "" -#: content/raw/models.py:19 +#: content/raw/models.py:21 msgid "raw contents" msgstr "" -#: content/richtext/models.py:22 +#: content/richtext/models.py:24 msgid "HTML Tidy" msgstr "" -#: content/richtext/models.py:23 +#: content/richtext/models.py:25 msgid "Ignore the HTML validation warnings" msgstr "" -#: content/richtext/models.py:47 +#: content/richtext/models.py:55 #, python-format msgid "" "HTML validation produced %(count)d warnings. Please review the updated " "content below before continuing: %(messages)s" msgstr "" -#: content/richtext/models.py:84 content/section/models.py:35 +#: content/richtext/models.py:99 content/section/models.py:36 msgid "text" msgstr "" -#: content/richtext/models.py:88 +#: content/richtext/models.py:103 msgid "rich text" msgstr "" -#: content/richtext/models.py:89 +#: content/richtext/models.py:104 msgid "rich texts" msgstr "" -#: content/rss/models.py:21 +#: content/rss/models.py:25 msgid "" "The rss field is updated several times a day. A change in the title will " "only be visible on the home page after the next feed update." msgstr "" -#: content/rss/models.py:22 +#: content/rss/models.py:28 msgid "link" msgstr "" -#: content/rss/models.py:23 +#: content/rss/models.py:30 msgid "pre-rendered content" msgstr "" -#: content/rss/models.py:24 +#: content/rss/models.py:32 msgid "last updated" msgstr "" -#: content/rss/models.py:25 +#: content/rss/models.py:33 msgid "max. items" msgstr "" -#: content/rss/models.py:29 +#: content/rss/models.py:37 msgid "RSS feed" msgstr "" -#: content/rss/models.py:30 +#: content/rss/models.py:38 msgid "RSS feeds" msgstr "" -#: content/section/models.py:41 +#: content/section/models.py:43 msgid "section" msgstr "" -#: content/section/models.py:42 +#: content/section/models.py:44 msgid "sections" msgstr "" -#: content/table/models.py:63 -msgid "plain" +#: content/template/models.py:53 +msgid "template content" +msgstr "" + +#: content/template/models.py:54 +msgid "template contents" msgstr "" -#: content/table/models.py:64 -msgid "title row" +#: content/template/models.py:63 models.py:400 +msgid "template" msgstr "" -#: content/table/models.py:66 -msgid "title row and column" +#: content/video/models.py:34 +msgid "video link" msgstr "" -#: content/table/models.py:72 -msgid "table" +#: content/video/models.py:36 +msgid "" +"This should be a link to a youtube or vimeo video, i.e.: http://www.youtube." +"com/watch?v=zmj1rpzDRZ0" msgstr "" -#: content/table/models.py:73 -msgid "tables" +#: content/video/models.py:41 +msgid "video" msgstr "" -#: content/table/models.py:87 -msgid "data" +#: content/video/models.py:42 +msgid "videos" msgstr "" -#: content/template/models.py:51 -msgid "template content" +#: contrib/tagging.py:132 +msgid "Tagging" msgstr "" -#: content/template/models.py:52 -msgid "template contents" +#: models.py:550 +msgid "ordering" msgstr "" -#: content/video/models.py:31 -msgid "video link" +#: module/blog/extensions/tags.py:17 +msgid "tags" msgstr "" -#: content/video/models.py:32 -msgid "" -"This should be a link to a youtube or vimeo video, i.e.: http://www.youtube." -"com/watch?v=zmj1rpzDRZ0" +#: module/blog/extensions/translations.py:25 +#: module/extensions/translations.py:140 translations.py:282 +msgid "language" msgstr "" -#: content/video/models.py:37 -msgid "video" +#: module/blog/extensions/translations.py:35 +#: module/extensions/translations.py:148 +msgid "translation of" msgstr "" -#: content/video/models.py:38 -msgid "videos" +#: module/blog/extensions/translations.py:39 +#: module/extensions/translations.py:152 +msgid "Leave this empty for entries in the primary language." msgstr "" -#: contrib/tagging.py:117 -msgid "Tagging" +#: module/blog/extensions/translations.py:67 +#: templates/admin/feincms/item_editor.html:33 +msgid "available translations" msgstr "" -#: module/blog/models.py:28 +#: module/blog/models.py:33 msgid "published" msgstr "" -#: module/blog/models.py:30 +#: module/blog/models.py:36 msgid "This is used for the generated navigation too." msgstr "" -#: module/blog/models.py:33 +#: module/blog/models.py:40 msgid "published on" msgstr "" -#: module/blog/models.py:34 +#: module/blog/models.py:42 msgid "Will be set automatically once you tick the `published` checkbox above." msgstr "" -#: module/blog/models.py:39 +#: module/blog/models.py:48 msgid "entry" msgstr "" -#: module/blog/models.py:40 +#: module/blog/models.py:49 msgid "entries" msgstr "" -#: module/blog/extensions/tags.py:12 -msgid "tags" -msgstr "" - -#: module/blog/extensions/translations.py:20 -#: module/extensions/translations.py:126 -msgid "translation of" -msgstr "" - -#: module/blog/extensions/translations.py:23 -#: module/extensions/translations.py:129 -msgid "Leave this empty for entries in the primary language." -msgstr "" - -#: module/blog/extensions/translations.py:44 -#: templates/admin/feincms/item_editor.html:43 -msgid "available translations" -msgstr "" - -#: module/extensions/changedate.py:32 +#: module/extensions/changedate.py:41 msgid "creation date" msgstr "" -#: module/extensions/changedate.py:33 +#: module/extensions/changedate.py:43 msgid "modification date" msgstr "" -#: module/extensions/ct_tracker.py:140 +#: module/extensions/ct_tracker.py:156 msgid "content types" msgstr "" -#: module/extensions/datepublisher.py:67 +#: module/extensions/datepublisher.py:86 msgid "publication date" msgstr "" -#: module/extensions/datepublisher.py:70 +#: module/extensions/datepublisher.py:90 msgid "publication end date" msgstr "" -#: module/extensions/datepublisher.py:72 +#: module/extensions/datepublisher.py:93 msgid "Leave empty if the entry should stay active forever." msgstr "" -#: module/extensions/datepublisher.py:103 +#: module/extensions/datepublisher.py:128 msgid "visible from - to" msgstr "" -#: module/extensions/datepublisher.py:113 +#: module/extensions/datepublisher.py:139 msgid "Date-based publishing" msgstr "" -#: module/extensions/featured.py:9 +#: module/extensions/featured.py:15 msgid "featured" msgstr "" -#: module/extensions/featured.py:14 +#: module/extensions/featured.py:21 msgid "Featured" msgstr "" -#: module/extensions/seo.py:9 +#: module/extensions/seo.py:16 msgid "meta keywords" msgstr "" -#: module/extensions/seo.py:10 -msgid "This will be prepended to the default keyword list." +#: module/extensions/seo.py:18 +msgid "Keywords are ignored by most search engines." msgstr "" -#: module/extensions/seo.py:11 +#: module/extensions/seo.py:20 msgid "meta description" msgstr "" -#: module/extensions/seo.py:12 -msgid "This will be prepended to the default description." +#: module/extensions/seo.py:22 +msgid "" +"This text is displayed on the search results page. It is however not used " +"for the SEO ranking. Text longer than 140 characters is truncated." msgstr "" -#: module/extensions/seo.py:17 +#: module/extensions/seo.py:32 msgid "Search engine optimization" msgstr "" -#: module/extensions/translations.py:207 +#: module/extensions/translations.py:249 msgid "Edit translation" msgstr "" -#: module/extensions/translations.py:210 +#: module/extensions/translations.py:256 msgid "Create translation" msgstr "" -#: module/extensions/translations.py:215 +#: module/extensions/translations.py:264 msgid "translations" msgstr "" -#: module/medialibrary/forms.py:26 +#: module/medialibrary/forms.py:31 msgid "This would create a loop in the hierarchy" msgstr "" -#: module/medialibrary/forms.py:64 +#: module/medialibrary/forms.py:77 #, python-format msgid "" "Cannot overwrite with different file type (attempt to overwrite a " "%(old_ext)s with a %(new_ext)s)" msgstr "" -#: module/medialibrary/modeladmins.py:58 +#: module/medialibrary/modeladmins.py:63 #, python-format msgid "Successfully added %(count)d media file to %(category)s." msgid_plural "Successfully added %(count)d media files to %(category)s." msgstr[0] "" msgstr[1] "" -#: module/medialibrary/modeladmins.py:77 +#: module/medialibrary/modeladmins.py:84 msgid "Add selected media files to category" msgstr "" -#: module/medialibrary/modeladmins.py:86 +#: module/medialibrary/modeladmins.py:94 #, python-format msgid "ZIP file exported as %s" msgstr "" -#: module/medialibrary/modeladmins.py:88 +#: module/medialibrary/modeladmins.py:96 #, python-format msgid "ZIP file export failed: %s" msgstr "" -#: module/medialibrary/modeladmins.py:93 +#: module/medialibrary/modeladmins.py:104 msgid "Export selected media files as zip file" msgstr "" -#: module/medialibrary/modeladmins.py:136 +#: module/medialibrary/modeladmins.py:157 #: templates/admin/feincms/page/page/item_editor.html:15 msgid "Preview" msgstr "" -#: module/medialibrary/modeladmins.py:141 module/medialibrary/models.py:88 +#: module/medialibrary/modeladmins.py:162 module/medialibrary/models.py:103 msgid "file size" msgstr "" -#: module/medialibrary/modeladmins.py:146 module/medialibrary/models.py:86 +#: module/medialibrary/modeladmins.py:167 module/medialibrary/models.py:100 msgid "created" msgstr "" -#: module/medialibrary/modeladmins.py:165 module/medialibrary/models.py:85 +#: module/medialibrary/modeladmins.py:187 module/medialibrary/models.py:97 msgid "file type" msgstr "" -#: module/medialibrary/modeladmins.py:186 +#: module/medialibrary/modeladmins.py:211 msgid "file info" msgstr "" -#: module/medialibrary/modeladmins.py:198 +#: module/medialibrary/modeladmins.py:224 #, python-format msgid "%d files imported" msgstr "" -#: module/medialibrary/modeladmins.py:200 +#: module/medialibrary/modeladmins.py:226 #, python-format msgid "ZIP import failed: %s" msgstr "" -#: module/medialibrary/modeladmins.py:202 +#: module/medialibrary/modeladmins.py:228 msgid "No input file given" msgstr "" -#: module/medialibrary/models.py:43 +#: module/medialibrary/models.py:49 msgid "parent" msgstr "" -#: module/medialibrary/models.py:45 module/page/models.py:171 +#: module/medialibrary/models.py:51 module/page/models.py:175 msgid "slug" msgstr "" -#: module/medialibrary/models.py:49 +#: module/medialibrary/models.py:55 msgid "category" msgstr "" -#: module/medialibrary/models.py:50 module/medialibrary/models.py:90 +#: module/medialibrary/models.py:56 module/medialibrary/models.py:106 msgid "categories" msgstr "" -#: module/medialibrary/models.py:87 +#: module/medialibrary/models.py:101 msgid "copyright" msgstr "" -#: module/medialibrary/models.py:202 +#: module/medialibrary/models.py:219 msgid "Image" msgstr "" -#: module/medialibrary/models.py:203 +#: module/medialibrary/models.py:221 msgid "Video" msgstr "" -#: module/medialibrary/models.py:204 +#: module/medialibrary/models.py:224 msgid "Audio" msgstr "" -#: module/medialibrary/models.py:205 +#: module/medialibrary/models.py:226 msgid "PDF document" msgstr "" -#: module/medialibrary/models.py:206 +#: module/medialibrary/models.py:227 msgid "Flash" msgstr "" -#: module/medialibrary/models.py:207 +#: module/medialibrary/models.py:228 msgid "Text" msgstr "" -#: module/medialibrary/models.py:208 +#: module/medialibrary/models.py:229 msgid "Rich Text" msgstr "" -#: module/medialibrary/models.py:209 +#: module/medialibrary/models.py:230 msgid "Zip archive" msgstr "" -#: module/medialibrary/models.py:210 +#: module/medialibrary/models.py:231 msgid "Microsoft Word" msgstr "" -#: module/medialibrary/models.py:211 +#: module/medialibrary/models.py:233 msgid "Microsoft Excel" msgstr "" -#: module/medialibrary/models.py:212 +#: module/medialibrary/models.py:235 msgid "Microsoft PowerPoint" msgstr "" -#: module/medialibrary/models.py:213 +#: module/medialibrary/models.py:237 msgid "Binary" msgstr "" -#: module/medialibrary/models.py:232 +#: module/medialibrary/models.py:261 msgid "description" msgstr "" -#: module/medialibrary/models.py:235 +#: module/medialibrary/models.py:264 msgid "media file translation" msgstr "" -#: module/medialibrary/models.py:236 +#: module/medialibrary/models.py:265 msgid "media file translations" msgstr "" -#: module/page/forms.py:172 -msgid "This URL is already taken by an active page." +#: module/page/extensions/excerpt.py:18 +msgid "excerpt" msgstr "" -#: module/page/forms.py:190 -msgid "This URL is already taken by another active page." +#: module/page/extensions/excerpt.py:21 +msgid "Add a brief excerpt summarizing the content of this page." msgstr "" -#: module/page/modeladmins.py:41 -msgid "Other options" +#: module/page/extensions/excerpt.py:25 +msgid "Excerpt" msgstr "" -#: module/page/modeladmins.py:90 module/page/models.py:174 -msgid "in navigation" +#: module/page/extensions/navigation.py:86 +#: module/page/extensions/navigation.py:115 +msgid "navigation extension" msgstr "" -#: module/page/modeladmins.py:105 -msgid "Add child page" +#: module/page/extensions/navigation.py:119 +msgid "" +"Select the module providing subpages for this page if you need to customize " +"the navigation." msgstr "" -#: module/page/modeladmins.py:107 -#: templates/admin/feincms/content_inline.html:9 -msgid "View on site" +#: module/page/extensions/navigation.py:134 +msgid "Navigation extension" msgstr "" -#: module/page/modeladmins.py:126 -#, python-format -msgid "Add %(language)s Translation of \"%(page)s\"" +#: module/page/extensions/navigationgroups.py:20 +msgid "Default" msgstr "" -#: module/page/modeladmins.py:156 -msgid "" -"The content from the original translation has been copied to the newly " -"created page." +#: module/page/extensions/navigationgroups.py:21 +msgid "Footer" msgstr "" -#: module/page/modeladmins.py:170 -msgid "You don't have the necessary permissions to edit this object" +#: module/page/extensions/navigationgroups.py:28 +msgid "navigation group" msgstr "" -#: module/page/modeladmins.py:185 -msgid "inherited" +#: module/page/extensions/relatedpages.py:21 +msgid "Select pages that should be listed as related content." msgstr "" -#: module/page/modeladmins.py:189 -msgid "extensions" +#: module/page/extensions/relatedpages.py:26 +msgid "Related pages" msgstr "" -#: module/page/modeladmins.py:193 module/page/models.py:206 -msgid "is active" +#: module/page/extensions/sites.py:21 +msgid "Site" msgstr "" -#: module/page/models.py:167 -msgid "active" +#: module/page/extensions/symlinks.py:22 +msgid "symlinked page" msgstr "" -#: module/page/models.py:175 -msgid "override URL" +#: module/page/extensions/symlinks.py:23 +msgid "All content is inherited from this page if given." msgstr "" -#: module/page/models.py:176 +#: module/page/extensions/titles.py:19 +msgid "content title" +msgstr "" + +#: module/page/extensions/titles.py:22 +msgid "The first line is the main title, the following lines are subtitles." +msgstr "" + +#: module/page/extensions/titles.py:26 +msgid "page title" +msgstr "" + +#: module/page/extensions/titles.py:30 msgid "" -"Override the target URL. Be sure to include slashes at the beginning and at " -"the end if it is a local URL. This affects both the navigation and subpages' " -"URLs." +"Page title for browser window. Same as title bydefault. Must not be longer " +"than 70 characters." msgstr "" -#: module/page/models.py:177 -msgid "redirect to" +#: module/page/extensions/titles.py:60 +msgid "Titles" msgstr "" -#: module/page/models.py:178 -msgid "Target URL for automatic redirects or the primary key of a page." +#: module/page/forms.py:187 +msgid "This URL is already taken by an active page." msgstr "" -#: module/page/models.py:180 -msgid "Cached URL" +#: module/page/forms.py:206 +msgid "This URL is already taken by another active page." msgstr "" -#: module/page/models.py:395 -msgid "page" +#: module/page/forms.py:211 +msgid "This page does not allow attachment of child pages" msgstr "" -#: module/page/models.py:396 -msgid "pages" +#: module/page/modeladmins.py:42 +msgid "Other options" msgstr "" -#: module/page/extensions/excerpt.py:9 -msgid "excerpt" +#: module/page/modeladmins.py:80 module/page/models.py:182 +msgid "in navigation" msgstr "" -#: module/page/extensions/excerpt.py:10 -msgid "Add a brief excerpt summarizing the content of this page." +#: module/page/modeladmins.py:109 module/page/modeladmins.py:111 +msgid "Add child page" msgstr "" -#: module/page/extensions/excerpt.py:12 -msgid "Excerpt" +#: module/page/modeladmins.py:120 module/page/modeladmins.py:122 +#: templates/admin/feincms/content_inline.html:9 +msgid "View on site" msgstr "" -#: module/page/extensions/navigation.py:82 -#: module/page/extensions/navigation.py:107 -msgid "navigation extension" +#: module/page/modeladmins.py:142 +#, python-format +msgid "Add %(language)s translation of \"%(page)s\"" msgstr "" -#: module/page/extensions/navigation.py:110 +#: module/page/modeladmins.py:177 msgid "" -"Select the module providing subpages for this page if you need to customize " -"the navigation." +"The content from the original translation has been copied to the newly " +"created page." msgstr "" -#: module/page/extensions/navigation.py:125 -msgid "Navigation extension" +#: module/page/modeladmins.py:197 +msgid "You don't have the necessary permissions to edit this object" msgstr "" -#: module/page/extensions/relatedpages.py:14 -msgid "Select pages that should be listed as related content." +#: module/page/modeladmins.py:223 +msgid "inherited" msgstr "" -#: module/page/extensions/relatedpages.py:19 -msgid "Related pages" +#: module/page/modeladmins.py:229 +msgid "extensions" msgstr "" -#: module/page/extensions/sites.py:16 -msgid "Site" +#: module/page/modeladmins.py:233 module/page/models.py:221 +msgid "is active" msgstr "" -#: module/page/extensions/symlinks.py:15 -msgid "symlinked page" +#: module/page/models.py:169 +msgid "active" msgstr "" -#: module/page/extensions/symlinks.py:16 -msgid "All content is inherited from this page if given." +#: module/page/models.py:173 +msgid "This title is also used for navigation menu items." msgstr "" -#: module/page/extensions/titles.py:13 -msgid "content title" +#: module/page/models.py:176 +msgid "This is used to build the URL for this page" msgstr "" -#: module/page/extensions/titles.py:14 -msgid "The first line is the main title, the following lines are subtitles." +#: module/page/models.py:184 +msgid "override URL" msgstr "" -#: module/page/extensions/titles.py:15 -msgid "page title" +#: module/page/models.py:186 +msgid "" +"Override the target URL. Be sure to include slashes at the beginning and at " +"the end if it is a local URL. This affects both the navigation and subpages' " +"URLs." +msgstr "" + +#: module/page/models.py:190 +msgid "redirect to" msgstr "" -#: module/page/extensions/titles.py:16 -msgid "Page title for browser window. Same as title by default." +#: module/page/models.py:193 +msgid "Target URL for automatic redirects or the primary key of a page." msgstr "" -#: module/page/extensions/titles.py:43 -msgid "Titles" +#: module/page/models.py:196 +msgid "Cached URL" msgstr "" -#: templates/admin/filter.html:3 +#: module/page/models.py:298 #, python-format -msgid " By %(filter_title)s " +msgid "" +"This %(page_class)s uses a singleton template, and " +"FEINCMS_SINGLETON_TEMPLATE_DELETION_ALLOWED=False" +msgstr "" + +#: module/page/models.py:426 +msgid "page" +msgstr "" + +#: module/page/models.py:427 +msgid "pages" msgstr "" #: templates/admin/feincms/_messages_js.html:4 @@ -810,16 +823,16 @@ msgstr "" msgid "Add new item" msgstr "" -#: templates/admin/feincms/content_inline.html:91 +#: templates/admin/feincms/content_inline.html:93 #, python-format msgid "Add another %(verbose_name)s" msgstr "" -#: templates/admin/feincms/content_inline.html:94 +#: templates/admin/feincms/content_inline.html:96 msgid "Remove" msgstr "" -#: templates/admin/feincms/fe_editor.html:40 +#: templates/admin/feincms/fe_editor.html:30 msgid "Save" msgstr "" @@ -847,13 +860,21 @@ msgstr "" msgid "remove" msgstr "" -#: templates/admin/feincms/recover_form.html:8 -#: templates/admin/feincms/revision_form.html:8 +#: templates/admin/feincms/page/page/item_editor.html:10 +msgid "Edit on site" +msgstr "" + #: templates/admin/feincms/page/page/item_editor.html:23 #: templates/admin/feincms/page/page/tree_editor.html:7 +#: templates/admin/feincms/recover_form.html:8 +#: templates/admin/feincms/revision_form.html:8 msgid "Home" msgstr "" +#: templates/admin/feincms/page/page/item_editor.html:27 +msgid "Add" +msgstr "" + #: templates/admin/feincms/recover_form.html:11 #, python-format msgid "Recover deleted %(verbose_name)s" @@ -876,28 +897,25 @@ msgstr "" msgid "Press the save button below to revert to this version of the object." msgstr "" -#: templates/admin/feincms/tree_editor.html:32 +#: templates/admin/feincms/tree_editor.html:22 msgid "Shortcuts" msgstr "" -#: templates/admin/feincms/tree_editor.html:34 +#: templates/admin/feincms/tree_editor.html:24 msgid "Collapse tree" msgstr "" -#: templates/admin/feincms/tree_editor.html:35 +#: templates/admin/feincms/tree_editor.html:25 msgid "Expand tree" msgstr "" -#: templates/admin/feincms/tree_editor.html:38 +#: templates/admin/feincms/tree_editor.html:29 msgid "Filter" msgstr "" -#: templates/admin/feincms/page/page/item_editor.html:10 -msgid "Edit on site" -msgstr "" - -#: templates/admin/feincms/page/page/item_editor.html:27 -msgid "Add" +#: templates/admin/filter.html:3 +#, python-format +msgid " By %(filter_title)s " msgstr "" #: templates/admin/medialibrary/add_to_category.html:5 diff --git a/feincms/locale/es/LC_MESSAGES/django.po b/feincms/locale/es/LC_MESSAGES/django.po index 5d2fbad59..d0781b932 100644 --- a/feincms/locale/es/LC_MESSAGES/django.po +++ b/feincms/locale/es/LC_MESSAGES/django.po @@ -1,744 +1,776 @@ # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. -# +# # Translators: # proycon , 2009 msgid "" msgstr "" "Project-Id-Version: FeinCMS\n" -"Report-Msgid-Bugs-To: http://github.com/feincms/feincms/issues\n" -"POT-Creation-Date: 2013-10-25 11:12+0200\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2014-07-20 14:59+0200\n" "PO-Revision-Date: 2013-10-25 09:15+0000\n" "Last-Translator: Matthias Kestenholz \n" -"Language-Team: Spanish (http://www.transifex.com/projects/p/feincms/language/es/)\n" +"Language-Team: Spanish (http://www.transifex.com/projects/p/feincms/language/" +"es/)\n" +"Language: es\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Language: es\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: models.py:370 content/template/models.py:59 -msgid "template" -msgstr "plantilla" - -#: models.py:510 -msgid "ordering" -msgstr "orden" - -#: translations.py:259 module/blog/extensions/translations.py:17 -#: module/extensions/translations.py:123 -msgid "language" -msgstr "idioma" - -#: admin/filterspecs.py:35 admin/filterspecs.py:70 +#: admin/filterspecs.py:47 admin/filterspecs.py:86 msgid "All" msgstr "Todos" -#: admin/filterspecs.py:46 module/page/models.py:172 +#: admin/filterspecs.py:58 module/page/models.py:178 msgid "Parent" msgstr "Padre" -#: admin/filterspecs.py:81 +#: admin/filterspecs.py:97 #: templates/admin/medialibrary/mediafile/change_list.html:14 msgid "Category" msgstr "Categoría" -#: admin/item_editor.py:159 +#: admin/item_editor.py:190 #, python-format msgid "Change %s" msgstr "Modificar %s" -#: admin/tree_editor.py:234 content/rss/models.py:20 -#: content/section/models.py:34 module/blog/models.py:29 -#: module/medialibrary/models.py:40 module/page/models.py:170 -#: module/page/models.py:226 +#: admin/tree_editor.py:294 content/rss/models.py:23 +#: content/section/models.py:35 module/blog/models.py:35 +#: module/medialibrary/models.py:45 module/page/models.py:172 +#: module/page/models.py:240 msgid "title" msgstr "título" -#: admin/tree_editor.py:407 +#: admin/tree_editor.py:353 admin/tree_editor.py:370 +msgid "You do not have permission to modify this object" +msgstr "" + +#: admin/tree_editor.py:491 +msgid "No permission" +msgstr "" + +#: admin/tree_editor.py:509 #, python-format msgid "%s has been moved to a new position." msgstr "%s ha sido movido a una nueva posición" -#: admin/tree_editor.py:411 +#: admin/tree_editor.py:512 msgid "Did not understand moving instruction." msgstr "No entiendo la orden de movimiento" -#: admin/tree_editor.py:420 +#: admin/tree_editor.py:523 msgid "actions" msgstr "acciones" -#: admin/tree_editor.py:432 +#: admin/tree_editor.py:552 #, python-format -msgid "Successfully deleted %s items." +msgid "Successfully deleted %(count)d items." msgstr "" -#: admin/tree_editor.py:440 +#: admin/tree_editor.py:565 #, python-format msgid "Delete selected %(verbose_name_plural)s" msgstr "" -#: content/application/models.py:218 +#: content/application/models.py:147 msgid "application content" msgstr "contenido de la aplicación" -#: content/application/models.py:219 +#: content/application/models.py:148 msgid "application contents" msgstr "contenidos de la aplicación" -#: content/application/models.py:249 +#: content/application/models.py:179 msgid "application" msgstr "aplicación" -#: content/comments/models.py:24 +#: content/comments/models.py:32 msgid "enabled" msgstr "activo" -#: content/comments/models.py:24 +#: content/comments/models.py:33 msgid "New comments may be added" msgstr "Se podrán añadir nuevos comentarios" -#: content/comments/models.py:28 content/comments/models.py:29 +#: content/comments/models.py:37 content/comments/models.py:38 msgid "comments" msgstr "comentarios" -#: content/comments/models.py:49 +#: content/comments/models.py:60 msgid "public" msgstr "público" -#: content/comments/models.py:49 +#: content/comments/models.py:61 msgid "not public" msgstr "privado" -#: content/contactform/models.py:18 +#: content/contactform/models.py:20 msgid "name" msgstr "nombre" -#: content/contactform/models.py:19 +#: content/contactform/models.py:21 msgid "email" msgstr "e-mail" -#: content/contactform/models.py:20 +#: content/contactform/models.py:22 msgid "subject" msgstr "asunto" -#: content/contactform/models.py:23 content/raw/models.py:14 +#: content/contactform/models.py:26 content/raw/models.py:16 msgid "content" msgstr "contenido" -#: content/contactform/models.py:34 +#: content/contactform/models.py:37 msgid "contact form" msgstr "formulario de contacto" -#: content/contactform/models.py:35 +#: content/contactform/models.py:38 msgid "contact forms" msgstr "formularios de contacto" -#: content/file/models.py:20 content/file/models.py:25 -#: module/medialibrary/models.py:84 +#: content/file/models.py:23 content/file/models.py:28 +#: module/medialibrary/models.py:94 msgid "file" msgstr "archivo" -#: content/file/models.py:26 +#: content/file/models.py:29 msgid "files" msgstr "archivos" -#: content/image/models.py:45 content/image/models.py:54 +#: content/image/models.py:46 content/image/models.py:55 msgid "image" msgstr "imagen" -#: content/image/models.py:48 +#: content/image/models.py:49 msgid "alternate text" msgstr "" -#: content/image/models.py:49 +#: content/image/models.py:50 msgid "Description of image" msgstr "" -#: content/image/models.py:50 module/medialibrary/models.py:231 +#: content/image/models.py:51 module/medialibrary/models.py:260 msgid "caption" msgstr "leyenda" -#: content/image/models.py:55 +#: content/image/models.py:56 msgid "images" msgstr "imágenes" -#: content/image/models.py:80 +#: content/image/models.py:82 msgid "position" msgstr "posición" -#: content/image/models.py:88 +#: content/image/models.py:90 msgid "format" msgstr "" -#: content/medialibrary/models.py:49 content/section/models.py:36 -#: module/medialibrary/fields.py:59 module/medialibrary/models.py:97 +#: content/medialibrary/models.py:51 content/section/models.py:38 +#: module/medialibrary/fields.py:66 module/medialibrary/models.py:112 msgid "media file" msgstr "archivo de medios" -#: content/medialibrary/models.py:50 module/medialibrary/models.py:98 +#: content/medialibrary/models.py:52 module/medialibrary/models.py:113 msgid "media files" msgstr "archivos de medios" -#: content/medialibrary/models.py:58 content/section/models.py:52 -#: content/table/models.py:82 +#: content/medialibrary/models.py:64 content/section/models.py:59 msgid "type" msgstr "tipo" -#: content/raw/models.py:18 +#: content/raw/models.py:20 msgid "raw content" msgstr "contenido crudo" -#: content/raw/models.py:19 +#: content/raw/models.py:21 msgid "raw contents" msgstr "contenidos crudos" -#: content/richtext/models.py:22 +#: content/richtext/models.py:24 msgid "HTML Tidy" msgstr "HTML Tidy" -#: content/richtext/models.py:23 +#: content/richtext/models.py:25 msgid "Ignore the HTML validation warnings" msgstr "Ignorar los avisos de validación HTML" -#: content/richtext/models.py:47 +#: content/richtext/models.py:55 #, python-format msgid "" "HTML validation produced %(count)d warnings. Please review the updated " "content below before continuing: %(messages)s" -msgstr "La validación del HTML produjo %(count)d avisos. Por favor revisa el contenido actualizado de la parte inferior antes de continuar: %(messages)s" +msgstr "" +"La validación del HTML produjo %(count)d avisos. Por favor revisa el " +"contenido actualizado de la parte inferior antes de continuar: %(messages)s" -#: content/richtext/models.py:84 content/section/models.py:35 +#: content/richtext/models.py:99 content/section/models.py:36 msgid "text" msgstr "texto" -#: content/richtext/models.py:88 +#: content/richtext/models.py:103 msgid "rich text" msgstr "texto rico" -#: content/richtext/models.py:89 +#: content/richtext/models.py:104 msgid "rich texts" msgstr "textos ricos" -#: content/rss/models.py:21 +#: content/rss/models.py:25 msgid "" "The rss field is updated several times a day. A change in the title will " "only be visible on the home page after the next feed update." -msgstr "El feed RSS es actualizado varias veces por dia. Cambios en el título solo aparecen en la página después de la actualización del feed RSS siguiente." +msgstr "" +"El feed RSS es actualizado varias veces por dia. Cambios en el título solo " +"aparecen en la página después de la actualización del feed RSS siguiente." -#: content/rss/models.py:22 +#: content/rss/models.py:28 msgid "link" msgstr "enlace" -#: content/rss/models.py:23 +#: content/rss/models.py:30 msgid "pre-rendered content" msgstr "contenido pre-renderizado" -#: content/rss/models.py:24 +#: content/rss/models.py:32 msgid "last updated" msgstr "última actualización" -#: content/rss/models.py:25 +#: content/rss/models.py:33 msgid "max. items" msgstr "número máximo" -#: content/rss/models.py:29 +#: content/rss/models.py:37 msgid "RSS feed" msgstr "feed RSS" -#: content/rss/models.py:30 +#: content/rss/models.py:38 msgid "RSS feeds" msgstr "feeds RSS" -#: content/section/models.py:41 +#: content/section/models.py:43 msgid "section" msgstr "sección" -#: content/section/models.py:42 +#: content/section/models.py:44 msgid "sections" msgstr "secciones" -#: content/table/models.py:63 -msgid "plain" -msgstr "plano" - -#: content/table/models.py:64 -msgid "title row" -msgstr "título de la fila" - -#: content/table/models.py:66 -msgid "title row and column" -msgstr "título de fila y columna" - -#: content/table/models.py:72 -msgid "table" -msgstr "tabla" - -#: content/table/models.py:73 -msgid "tables" -msgstr "tablas" - -#: content/table/models.py:87 -msgid "data" -msgstr "datos" - -#: content/template/models.py:51 +#: content/template/models.py:53 msgid "template content" msgstr "plantilla de contenido" -#: content/template/models.py:52 +#: content/template/models.py:54 msgid "template contents" msgstr "plantilla de contenidos" -#: content/video/models.py:31 +#: content/template/models.py:63 models.py:400 +msgid "template" +msgstr "plantilla" + +#: content/video/models.py:34 msgid "video link" msgstr "enlace de vídeo" -#: content/video/models.py:32 +#: content/video/models.py:36 msgid "" -"This should be a link to a youtube or vimeo video, i.e.: " -"http://www.youtube.com/watch?v=zmj1rpzDRZ0" -msgstr "Debe ser un enlace a un vídeo de YouTube o Vimeo. Por ejemplo: http://www.youtube.com/watch?v=zmj1rpzDRZ0" +"This should be a link to a youtube or vimeo video, i.e.: http://www.youtube." +"com/watch?v=zmj1rpzDRZ0" +msgstr "" +"Debe ser un enlace a un vídeo de YouTube o Vimeo. Por ejemplo: http://www." +"youtube.com/watch?v=zmj1rpzDRZ0" -#: content/video/models.py:37 +#: content/video/models.py:41 msgid "video" msgstr "vídeo" -#: content/video/models.py:38 +#: content/video/models.py:42 msgid "videos" msgstr "vídeos" -#: contrib/tagging.py:117 +#: contrib/tagging.py:132 msgid "Tagging" msgstr "" -#: module/blog/models.py:28 +#: models.py:550 +msgid "ordering" +msgstr "orden" + +#: module/blog/extensions/tags.py:17 +msgid "tags" +msgstr "etiquetas" + +#: module/blog/extensions/translations.py:25 +#: module/extensions/translations.py:140 translations.py:282 +msgid "language" +msgstr "idioma" + +#: module/blog/extensions/translations.py:35 +#: module/extensions/translations.py:148 +msgid "translation of" +msgstr "traducción de" + +#: module/blog/extensions/translations.py:39 +#: module/extensions/translations.py:152 +msgid "Leave this empty for entries in the primary language." +msgstr "Deja este campo vacío para las entradas en el idioma base." + +#: module/blog/extensions/translations.py:67 +#: templates/admin/feincms/item_editor.html:33 +msgid "available translations" +msgstr "traducciones disponibles" + +#: module/blog/models.py:33 msgid "published" msgstr "publicado" -#: module/blog/models.py:30 +#: module/blog/models.py:36 msgid "This is used for the generated navigation too." msgstr "También será usado para la navegación generada automáticamente." -#: module/blog/models.py:33 +#: module/blog/models.py:40 msgid "published on" msgstr "publicado en" -#: module/blog/models.py:34 -msgid "" -"Will be set automatically once you tick the `published` checkbox above." +#: module/blog/models.py:42 +msgid "Will be set automatically once you tick the `published` checkbox above." msgstr "Se establecerá automáticamente cuando se marque en 'publicado'." -#: module/blog/models.py:39 +#: module/blog/models.py:48 msgid "entry" msgstr "entrada" -#: module/blog/models.py:40 +#: module/blog/models.py:49 msgid "entries" msgstr "entradas" -#: module/blog/extensions/tags.py:12 -msgid "tags" -msgstr "etiquetas" - -#: module/blog/extensions/translations.py:20 -#: module/extensions/translations.py:126 -msgid "translation of" -msgstr "traducción de" - -#: module/blog/extensions/translations.py:23 -#: module/extensions/translations.py:129 -msgid "Leave this empty for entries in the primary language." -msgstr "Deja este campo vacío para las entradas en el idioma base." - -#: module/blog/extensions/translations.py:44 -#: templates/admin/feincms/item_editor.html:43 -msgid "available translations" -msgstr "traducciones disponibles" - -#: module/extensions/changedate.py:32 +#: module/extensions/changedate.py:41 msgid "creation date" msgstr "fecha de creación" -#: module/extensions/changedate.py:33 +#: module/extensions/changedate.py:43 msgid "modification date" msgstr "fecha de modificación" -#: module/extensions/ct_tracker.py:140 +#: module/extensions/ct_tracker.py:156 msgid "content types" msgstr "tipos de contenido" -#: module/extensions/datepublisher.py:67 +#: module/extensions/datepublisher.py:86 msgid "publication date" msgstr "fecha de publicación" -#: module/extensions/datepublisher.py:70 +#: module/extensions/datepublisher.py:90 msgid "publication end date" msgstr "publicar hasta" -#: module/extensions/datepublisher.py:72 +#: module/extensions/datepublisher.py:93 msgid "Leave empty if the entry should stay active forever." msgstr "Si se deja en blanco la entrada permanecerá activa para siempre." -#: module/extensions/datepublisher.py:103 +#: module/extensions/datepublisher.py:128 msgid "visible from - to" msgstr "visible de - hasta" -#: module/extensions/datepublisher.py:113 +#: module/extensions/datepublisher.py:139 msgid "Date-based publishing" msgstr "Publicación según la fecha" -#: module/extensions/featured.py:9 +#: module/extensions/featured.py:15 msgid "featured" msgstr "categorizado" -#: module/extensions/featured.py:14 +#: module/extensions/featured.py:21 msgid "Featured" msgstr "Categorizado" -#: module/extensions/seo.py:9 +#: module/extensions/seo.py:16 msgid "meta keywords" msgstr "meta palabras clave" -#: module/extensions/seo.py:10 -msgid "This will be prepended to the default keyword list." -msgstr "Será incluido antes de la lista de palabras clave." +#: module/extensions/seo.py:18 +msgid "Keywords are ignored by most search engines." +msgstr "" -#: module/extensions/seo.py:11 +#: module/extensions/seo.py:20 msgid "meta description" msgstr "meta descripción" -#: module/extensions/seo.py:12 -msgid "This will be prepended to the default description." -msgstr "Será incluido antes de la meta descripción por defecto." +#: module/extensions/seo.py:22 +msgid "" +"This text is displayed on the search results page. It is however not used " +"for the SEO ranking. Text longer than 140 characters is truncated." +msgstr "" -#: module/extensions/seo.py:17 +#: module/extensions/seo.py:32 msgid "Search engine optimization" msgstr "Optimización para motores de búsqueda" -#: module/extensions/translations.py:207 +#: module/extensions/translations.py:249 msgid "Edit translation" msgstr "Editar traducción" -#: module/extensions/translations.py:210 +#: module/extensions/translations.py:256 msgid "Create translation" msgstr "Crear traducción" -#: module/extensions/translations.py:215 +#: module/extensions/translations.py:264 msgid "translations" msgstr "traducciones" -#: module/medialibrary/forms.py:26 +#: module/medialibrary/forms.py:31 msgid "This would create a loop in the hierarchy" msgstr "" -#: module/medialibrary/forms.py:64 +#: module/medialibrary/forms.py:77 #, python-format msgid "" "Cannot overwrite with different file type (attempt to overwrite a " "%(old_ext)s with a %(new_ext)s)" msgstr "" -#: module/medialibrary/modeladmins.py:58 +#: module/medialibrary/modeladmins.py:63 #, python-format msgid "Successfully added %(count)d media file to %(category)s." msgid_plural "Successfully added %(count)d media files to %(category)s." msgstr[0] "" msgstr[1] "" -#: module/medialibrary/modeladmins.py:77 +#: module/medialibrary/modeladmins.py:84 msgid "Add selected media files to category" msgstr "" -#: module/medialibrary/modeladmins.py:86 +#: module/medialibrary/modeladmins.py:94 #, python-format msgid "ZIP file exported as %s" msgstr "" -#: module/medialibrary/modeladmins.py:88 +#: module/medialibrary/modeladmins.py:96 #, python-format msgid "ZIP file export failed: %s" msgstr "" -#: module/medialibrary/modeladmins.py:93 +#: module/medialibrary/modeladmins.py:104 msgid "Export selected media files as zip file" msgstr "" -#: module/medialibrary/modeladmins.py:136 +#: module/medialibrary/modeladmins.py:157 #: templates/admin/feincms/page/page/item_editor.html:15 msgid "Preview" msgstr "Pre-visualización" -#: module/medialibrary/modeladmins.py:141 module/medialibrary/models.py:88 +#: module/medialibrary/modeladmins.py:162 module/medialibrary/models.py:103 msgid "file size" msgstr "tamaño de archivo" -#: module/medialibrary/modeladmins.py:146 module/medialibrary/models.py:86 +#: module/medialibrary/modeladmins.py:167 module/medialibrary/models.py:100 msgid "created" msgstr "creado en" -#: module/medialibrary/modeladmins.py:165 module/medialibrary/models.py:85 +#: module/medialibrary/modeladmins.py:187 module/medialibrary/models.py:97 msgid "file type" msgstr "tipo de archivo" -#: module/medialibrary/modeladmins.py:186 +#: module/medialibrary/modeladmins.py:211 msgid "file info" msgstr "información del archivo" -#: module/medialibrary/modeladmins.py:198 +#: module/medialibrary/modeladmins.py:224 #, python-format msgid "%d files imported" msgstr "" -#: module/medialibrary/modeladmins.py:200 +#: module/medialibrary/modeladmins.py:226 #, python-format msgid "ZIP import failed: %s" msgstr "" -#: module/medialibrary/modeladmins.py:202 +#: module/medialibrary/modeladmins.py:228 msgid "No input file given" msgstr "" -#: module/medialibrary/models.py:43 +#: module/medialibrary/models.py:49 msgid "parent" msgstr "padre" -#: module/medialibrary/models.py:45 module/page/models.py:171 +#: module/medialibrary/models.py:51 module/page/models.py:175 msgid "slug" msgstr "slug" -#: module/medialibrary/models.py:49 +#: module/medialibrary/models.py:55 msgid "category" msgstr "categoría" -#: module/medialibrary/models.py:50 module/medialibrary/models.py:90 +#: module/medialibrary/models.py:56 module/medialibrary/models.py:106 msgid "categories" msgstr "categorías" -#: module/medialibrary/models.py:87 +#: module/medialibrary/models.py:101 msgid "copyright" msgstr "copyright" -#: module/medialibrary/models.py:202 +#: module/medialibrary/models.py:219 msgid "Image" msgstr "Imagen" -#: module/medialibrary/models.py:203 +#: module/medialibrary/models.py:221 msgid "Video" msgstr "Vídeo" -#: module/medialibrary/models.py:204 +#: module/medialibrary/models.py:224 msgid "Audio" msgstr "Audio" -#: module/medialibrary/models.py:205 +#: module/medialibrary/models.py:226 msgid "PDF document" msgstr "documento PDF" -#: module/medialibrary/models.py:206 +#: module/medialibrary/models.py:227 msgid "Flash" msgstr "Flash" -#: module/medialibrary/models.py:207 +#: module/medialibrary/models.py:228 msgid "Text" msgstr "Texto" -#: module/medialibrary/models.py:208 +#: module/medialibrary/models.py:229 msgid "Rich Text" msgstr "Texto rico" -#: module/medialibrary/models.py:209 +#: module/medialibrary/models.py:230 msgid "Zip archive" msgstr "" -#: module/medialibrary/models.py:210 +#: module/medialibrary/models.py:231 msgid "Microsoft Word" msgstr "Microsoft Word" -#: module/medialibrary/models.py:211 +#: module/medialibrary/models.py:233 msgid "Microsoft Excel" msgstr "Microsoft Excel" -#: module/medialibrary/models.py:212 +#: module/medialibrary/models.py:235 msgid "Microsoft PowerPoint" msgstr "Microsoft PowerPoint" -#: module/medialibrary/models.py:213 +#: module/medialibrary/models.py:237 msgid "Binary" msgstr "Binario" -#: module/medialibrary/models.py:232 +#: module/medialibrary/models.py:261 msgid "description" msgstr "descripción" -#: module/medialibrary/models.py:235 +#: module/medialibrary/models.py:264 msgid "media file translation" msgstr "traducción del archivo de media" -#: module/medialibrary/models.py:236 +#: module/medialibrary/models.py:265 msgid "media file translations" msgstr "traducciones del archivo de media" -#: module/page/forms.py:172 +#: module/page/extensions/excerpt.py:18 +msgid "excerpt" +msgstr "extracto" + +#: module/page/extensions/excerpt.py:21 +msgid "Add a brief excerpt summarizing the content of this page." +msgstr "Añade una breve descripción resumiendo el contenido de la página." + +#: module/page/extensions/excerpt.py:25 +msgid "Excerpt" +msgstr "Extracto" + +#: module/page/extensions/navigation.py:86 +#: module/page/extensions/navigation.py:115 +msgid "navigation extension" +msgstr "extensión de navegación" + +#: module/page/extensions/navigation.py:119 +msgid "" +"Select the module providing subpages for this page if you need to customize " +"the navigation." +msgstr "" +"Selecciona el módulo que provee sub-páginas para esta pagina si necesitas " +"personalizar la navegación." + +#: module/page/extensions/navigation.py:134 +msgid "Navigation extension" +msgstr "Extensión de navegación" + +#: module/page/extensions/navigationgroups.py:20 +msgid "Default" +msgstr "" + +#: module/page/extensions/navigationgroups.py:21 +msgid "Footer" +msgstr "" + +#: module/page/extensions/navigationgroups.py:28 +#, fuzzy +#| msgid "in navigation" +msgid "navigation group" +msgstr "en la navegación" + +#: module/page/extensions/relatedpages.py:21 +msgid "Select pages that should be listed as related content." +msgstr "Selecciona las páginas que se mostrarán como contenido relacionado." + +#: module/page/extensions/relatedpages.py:26 +msgid "Related pages" +msgstr "Páginas relacionadas" + +#: module/page/extensions/sites.py:21 +msgid "Site" +msgstr "" + +#: module/page/extensions/symlinks.py:22 +msgid "symlinked page" +msgstr "página direccionada" + +#: module/page/extensions/symlinks.py:23 +msgid "All content is inherited from this page if given." +msgstr "Todo el contenido es heredado de esta página." + +#: module/page/extensions/titles.py:19 +msgid "content title" +msgstr "título del contenido" + +#: module/page/extensions/titles.py:22 +msgid "The first line is the main title, the following lines are subtitles." +msgstr "" +"La primera línea es el título principal. Otras líneas serán subtítulos." + +#: module/page/extensions/titles.py:26 +msgid "page title" +msgstr "título de la página" + +#: module/page/extensions/titles.py:30 +#, fuzzy +#| msgid "Page title for browser window. Same as title by default." +msgid "" +"Page title for browser window. Same as title bydefault. Must not be longer " +"than 70 characters." +msgstr "" +"Título de la página para la ventana del navegador. Si se omite utilizará el " +"mismo título." + +#: module/page/extensions/titles.py:60 +msgid "Titles" +msgstr "Títulos" + +#: module/page/forms.py:187 msgid "This URL is already taken by an active page." msgstr "Esta URL ya está en uso en una página activa" -#: module/page/forms.py:190 +#: module/page/forms.py:206 msgid "This URL is already taken by another active page." msgstr "Esta URL ya está en uno por otra página activa" -#: module/page/modeladmins.py:41 +#: module/page/forms.py:211 +msgid "This page does not allow attachment of child pages" +msgstr "" + +#: module/page/modeladmins.py:42 msgid "Other options" msgstr "otras opciones" -#: module/page/modeladmins.py:90 module/page/models.py:174 +#: module/page/modeladmins.py:80 module/page/models.py:182 msgid "in navigation" msgstr "en la navegación" -#: module/page/modeladmins.py:105 +#: module/page/modeladmins.py:109 module/page/modeladmins.py:111 msgid "Add child page" msgstr "Añade una página hija" -#: module/page/modeladmins.py:107 +#: module/page/modeladmins.py:120 module/page/modeladmins.py:122 #: templates/admin/feincms/content_inline.html:9 msgid "View on site" msgstr "Ver en sitio" -#: module/page/modeladmins.py:126 +#: module/page/modeladmins.py:142 #, python-format -msgid "Add %(language)s Translation of \"%(page)s\"" +msgid "Add %(language)s translation of \"%(page)s\"" msgstr "" -#: module/page/modeladmins.py:156 +#: module/page/modeladmins.py:177 msgid "" "The content from the original translation has been copied to the newly " "created page." msgstr "" -#: module/page/modeladmins.py:170 +#: module/page/modeladmins.py:197 msgid "You don't have the necessary permissions to edit this object" msgstr "" -#: module/page/modeladmins.py:185 +#: module/page/modeladmins.py:223 msgid "inherited" msgstr "heredado" -#: module/page/modeladmins.py:189 +#: module/page/modeladmins.py:229 msgid "extensions" msgstr "extensiones" -#: module/page/modeladmins.py:193 module/page/models.py:206 +#: module/page/modeladmins.py:233 module/page/models.py:221 msgid "is active" msgstr "activo" -#: module/page/models.py:167 +#: module/page/models.py:169 msgid "active" msgstr "activo" -#: module/page/models.py:175 +#: module/page/models.py:173 +#, fuzzy +#| msgid "This is used for the generated navigation too." +msgid "This title is also used for navigation menu items." +msgstr "También será usado para la navegación generada automáticamente." + +#: module/page/models.py:176 +msgid "This is used to build the URL for this page" +msgstr "" + +#: module/page/models.py:184 msgid "override URL" msgstr "URL efectivo" -#: module/page/models.py:176 +#: module/page/models.py:186 msgid "" "Override the target URL. Be sure to include slashes at the beginning and at " -"the end if it is a local URL. This affects both the navigation and subpages'" -" URLs." -msgstr "URL efectivo. Debe contener una '/' cuando se trata de un URL local. Este campo afecta la navegación y los URLs de las sub-páginas." +"the end if it is a local URL. This affects both the navigation and subpages' " +"URLs." +msgstr "" +"URL efectivo. Debe contener una '/' cuando se trata de un URL local. Este " +"campo afecta la navegación y los URLs de las sub-páginas." -#: module/page/models.py:177 +#: module/page/models.py:190 msgid "redirect to" msgstr "redirección a" -#: module/page/models.py:178 +#: module/page/models.py:193 msgid "Target URL for automatic redirects or the primary key of a page." msgstr "" -#: module/page/models.py:180 +#: module/page/models.py:196 msgid "Cached URL" msgstr "URL en cache" -#: module/page/models.py:395 +#: module/page/models.py:298 +#, python-format +msgid "" +"This %(page_class)s uses a singleton template, and " +"FEINCMS_SINGLETON_TEMPLATE_DELETION_ALLOWED=False" +msgstr "" + +#: module/page/models.py:426 msgid "page" msgstr "página" -#: module/page/models.py:396 +#: module/page/models.py:427 msgid "pages" msgstr "páginas" -#: module/page/extensions/excerpt.py:9 -msgid "excerpt" -msgstr "extracto" - -#: module/page/extensions/excerpt.py:10 -msgid "Add a brief excerpt summarizing the content of this page." -msgstr "Añade una breve descripción resumiendo el contenido de la página." - -#: module/page/extensions/excerpt.py:12 -msgid "Excerpt" -msgstr "Extracto" - -#: module/page/extensions/navigation.py:82 -#: module/page/extensions/navigation.py:107 -msgid "navigation extension" -msgstr "extensión de navegación" - -#: module/page/extensions/navigation.py:110 -msgid "" -"Select the module providing subpages for this page if you need to customize " -"the navigation." -msgstr "Selecciona el módulo que provee sub-páginas para esta pagina si necesitas personalizar la navegación." - -#: module/page/extensions/navigation.py:125 -msgid "Navigation extension" -msgstr "Extensión de navegación" - -#: module/page/extensions/relatedpages.py:14 -msgid "Select pages that should be listed as related content." -msgstr "Selecciona las páginas que se mostrarán como contenido relacionado." - -#: module/page/extensions/relatedpages.py:19 -msgid "Related pages" -msgstr "Páginas relacionadas" - -#: module/page/extensions/sites.py:16 -msgid "Site" -msgstr "" - -#: module/page/extensions/symlinks.py:15 -msgid "symlinked page" -msgstr "página direccionada" - -#: module/page/extensions/symlinks.py:16 -msgid "All content is inherited from this page if given." -msgstr "Todo el contenido es heredado de esta página." - -#: module/page/extensions/titles.py:13 -msgid "content title" -msgstr "título del contenido" - -#: module/page/extensions/titles.py:14 -msgid "The first line is the main title, the following lines are subtitles." -msgstr "La primera línea es el título principal. Otras líneas serán subtítulos." - -#: module/page/extensions/titles.py:15 -msgid "page title" -msgstr "título de la página" - -#: module/page/extensions/titles.py:16 -msgid "Page title for browser window. Same as title by default." -msgstr "Título de la página para la ventana del navegador. Si se omite utilizará el mismo título." - -#: module/page/extensions/titles.py:43 -msgid "Titles" -msgstr "Títulos" - -#: templates/admin/filter.html:3 -#, python-format -msgid " By %(filter_title)s " -msgstr "Por %(filter_title)s" - #: templates/admin/feincms/_messages_js.html:4 msgid "Really delete item?" msgstr "¿Confirma la eliminación del elemento?" @@ -757,7 +789,8 @@ msgstr "Imposible de eliminar el elemento" #: templates/admin/feincms/_messages_js.html:6 msgid "Cannot delete item, because it is parent of at least one other item." -msgstr "Imposible de eliminar el elemento, porque es padre de al menos un elemento ." +msgstr "" +"Imposible de eliminar el elemento, porque es padre de al menos un elemento ." #: templates/admin/feincms/_messages_js.html:7 msgid "Change template" @@ -770,9 +803,8 @@ msgstr "¿Deas cambiar la plantilla?
    Todos los cambios se guardarán." #: templates/admin/feincms/_messages_js.html:9 #, python-format msgid "" -"Really change template?
    All changes are saved and content from " -"%%(source_regions)s is moved to " -"%%(target_region)s." +"Really change template?
    All changes are saved and content from " +"%%(source_regions)s is moved to %%(target_region)s." msgstr "" #: templates/admin/feincms/_messages_js.html:12 @@ -807,22 +839,24 @@ msgstr "Región vacía" msgid "" "Content from the parent site is automatically inherited. To override this " "behaviour, add some content." -msgstr "El contenido del sitio padre se hereda automáticamente. Para sobreescribir este comportamiento añade algún contenido." +msgstr "" +"El contenido del sitio padre se hereda automáticamente. Para sobreescribir " +"este comportamiento añade algún contenido." #: templates/admin/feincms/content_editor.html:33 msgid "Add new item" msgstr "Añadir nuevo elemento" -#: templates/admin/feincms/content_inline.html:91 +#: templates/admin/feincms/content_inline.html:93 #, python-format msgid "Add another %(verbose_name)s" msgstr "" -#: templates/admin/feincms/content_inline.html:94 +#: templates/admin/feincms/content_inline.html:96 msgid "Remove" msgstr "" -#: templates/admin/feincms/fe_editor.html:40 +#: templates/admin/feincms/fe_editor.html:30 msgid "Save" msgstr "Guardar" @@ -850,13 +884,21 @@ msgstr "abajo" msgid "remove" msgstr "borrar" -#: templates/admin/feincms/recover_form.html:8 -#: templates/admin/feincms/revision_form.html:8 +#: templates/admin/feincms/page/page/item_editor.html:10 +msgid "Edit on site" +msgstr "Editar en el sitio" + #: templates/admin/feincms/page/page/item_editor.html:23 #: templates/admin/feincms/page/page/tree_editor.html:7 +#: templates/admin/feincms/recover_form.html:8 +#: templates/admin/feincms/revision_form.html:8 msgid "Home" msgstr "Página inicial" +#: templates/admin/feincms/page/page/item_editor.html:27 +msgid "Add" +msgstr "" + #: templates/admin/feincms/recover_form.html:11 #, python-format msgid "Recover deleted %(verbose_name)s" @@ -879,29 +921,26 @@ msgstr "" msgid "Press the save button below to revert to this version of the object." msgstr "" -#: templates/admin/feincms/tree_editor.html:32 +#: templates/admin/feincms/tree_editor.html:22 msgid "Shortcuts" msgstr "Atajos" -#: templates/admin/feincms/tree_editor.html:34 +#: templates/admin/feincms/tree_editor.html:24 msgid "Collapse tree" msgstr "Colapsar el árbol" -#: templates/admin/feincms/tree_editor.html:35 +#: templates/admin/feincms/tree_editor.html:25 msgid "Expand tree" msgstr "Expandir el árbol" -#: templates/admin/feincms/tree_editor.html:38 +#: templates/admin/feincms/tree_editor.html:29 msgid "Filter" msgstr "Filtrar" -#: templates/admin/feincms/page/page/item_editor.html:10 -msgid "Edit on site" -msgstr "Editar en el sitio" - -#: templates/admin/feincms/page/page/item_editor.html:27 -msgid "Add" -msgstr "" +#: templates/admin/filter.html:3 +#, python-format +msgid " By %(filter_title)s " +msgstr "Por %(filter_title)s" #: templates/admin/medialibrary/add_to_category.html:5 #: templates/admin/medialibrary/add_to_category.html:9 @@ -945,9 +984,13 @@ msgstr "%(comment_count)s comentarios." #, python-format msgid "" "\n" -" %(comment_username)s said on %(comment_submit_date)s
    \n" +" %(comment_username)s said on %(comment_submit_date)s
    \n" +" " +msgstr "" +"\n" +" %(comment_username)s dijo el %(comment_submit_date)s
    \n" " " -msgstr "\n %(comment_username)s dijo el %(comment_submit_date)s
    \n " #: templates/content/comments/default.html:28 msgid "No comments." @@ -964,3 +1007,27 @@ msgstr "Enviar" #: templates/content/contactform/thanks.html:3 msgid "Thanks!" msgstr "¡Gracias!" + +#~ msgid "plain" +#~ msgstr "plano" + +#~ msgid "title row" +#~ msgstr "título de la fila" + +#~ msgid "title row and column" +#~ msgstr "título de fila y columna" + +#~ msgid "table" +#~ msgstr "tabla" + +#~ msgid "tables" +#~ msgstr "tablas" + +#~ msgid "data" +#~ msgstr "datos" + +#~ msgid "This will be prepended to the default keyword list." +#~ msgstr "Será incluido antes de la lista de palabras clave." + +#~ msgid "This will be prepended to the default description." +#~ msgstr "Será incluido antes de la meta descripción por defecto." diff --git a/feincms/locale/fr/LC_MESSAGES/django.po b/feincms/locale/fr/LC_MESSAGES/django.po index 6b714665f..c52cd5149 100644 --- a/feincms/locale/fr/LC_MESSAGES/django.po +++ b/feincms/locale/fr/LC_MESSAGES/django.po @@ -1,743 +1,776 @@ # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. -# +# # Translators: msgid "" msgstr "" "Project-Id-Version: FeinCMS\n" -"Report-Msgid-Bugs-To: http://github.com/feincms/feincms/issues\n" -"POT-Creation-Date: 2013-10-25 11:12+0200\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2014-07-20 14:59+0200\n" "PO-Revision-Date: 2013-10-25 09:15+0000\n" "Last-Translator: Matthias Kestenholz \n" -"Language-Team: French (http://www.transifex.com/projects/p/feincms/language/fr/)\n" +"Language-Team: French (http://www.transifex.com/projects/p/feincms/language/" +"fr/)\n" +"Language: fr\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Language: fr\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" -#: models.py:370 content/template/models.py:59 -msgid "template" -msgstr "modèle" - -#: models.py:510 -msgid "ordering" -msgstr "séquence" - -#: translations.py:259 module/blog/extensions/translations.py:17 -#: module/extensions/translations.py:123 -msgid "language" -msgstr "langue" - -#: admin/filterspecs.py:35 admin/filterspecs.py:70 +#: admin/filterspecs.py:47 admin/filterspecs.py:86 msgid "All" msgstr "Tous" -#: admin/filterspecs.py:46 module/page/models.py:172 +#: admin/filterspecs.py:58 module/page/models.py:178 msgid "Parent" msgstr "Parent" -#: admin/filterspecs.py:81 +#: admin/filterspecs.py:97 #: templates/admin/medialibrary/mediafile/change_list.html:14 msgid "Category" msgstr "" -#: admin/item_editor.py:159 +#: admin/item_editor.py:190 #, python-format msgid "Change %s" msgstr "Changement %s" -#: admin/tree_editor.py:234 content/rss/models.py:20 -#: content/section/models.py:34 module/blog/models.py:29 -#: module/medialibrary/models.py:40 module/page/models.py:170 -#: module/page/models.py:226 +#: admin/tree_editor.py:294 content/rss/models.py:23 +#: content/section/models.py:35 module/blog/models.py:35 +#: module/medialibrary/models.py:45 module/page/models.py:172 +#: module/page/models.py:240 msgid "title" msgstr "titre" -#: admin/tree_editor.py:407 +#: admin/tree_editor.py:353 admin/tree_editor.py:370 +msgid "You do not have permission to modify this object" +msgstr "" + +#: admin/tree_editor.py:491 +msgid "No permission" +msgstr "" + +#: admin/tree_editor.py:509 #, python-format msgid "%s has been moved to a new position." msgstr "" -#: admin/tree_editor.py:411 +#: admin/tree_editor.py:512 msgid "Did not understand moving instruction." msgstr "" -#: admin/tree_editor.py:420 +#: admin/tree_editor.py:523 msgid "actions" msgstr "actions" -#: admin/tree_editor.py:432 +#: admin/tree_editor.py:552 #, python-format -msgid "Successfully deleted %s items." +msgid "Successfully deleted %(count)d items." msgstr "" -#: admin/tree_editor.py:440 +#: admin/tree_editor.py:565 #, python-format msgid "Delete selected %(verbose_name_plural)s" msgstr "" -#: content/application/models.py:218 +#: content/application/models.py:147 msgid "application content" msgstr "contenu d'application" -#: content/application/models.py:219 +#: content/application/models.py:148 msgid "application contents" msgstr "contenus d'application" -#: content/application/models.py:249 +#: content/application/models.py:179 msgid "application" msgstr "application" -#: content/comments/models.py:24 +#: content/comments/models.py:32 msgid "enabled" msgstr "" -#: content/comments/models.py:24 +#: content/comments/models.py:33 msgid "New comments may be added" msgstr "" -#: content/comments/models.py:28 content/comments/models.py:29 +#: content/comments/models.py:37 content/comments/models.py:38 msgid "comments" msgstr "" -#: content/comments/models.py:49 +#: content/comments/models.py:60 msgid "public" msgstr "" -#: content/comments/models.py:49 +#: content/comments/models.py:61 msgid "not public" msgstr "" -#: content/contactform/models.py:18 +#: content/contactform/models.py:20 msgid "name" msgstr "nom" -#: content/contactform/models.py:19 +#: content/contactform/models.py:21 msgid "email" msgstr "courriel" -#: content/contactform/models.py:20 +#: content/contactform/models.py:22 msgid "subject" msgstr "sujet" -#: content/contactform/models.py:23 content/raw/models.py:14 +#: content/contactform/models.py:26 content/raw/models.py:16 msgid "content" msgstr "Contenu" -#: content/contactform/models.py:34 +#: content/contactform/models.py:37 msgid "contact form" msgstr "formulaire de contact" -#: content/contactform/models.py:35 +#: content/contactform/models.py:38 msgid "contact forms" msgstr "formulaires de contact" -#: content/file/models.py:20 content/file/models.py:25 -#: module/medialibrary/models.py:84 +#: content/file/models.py:23 content/file/models.py:28 +#: module/medialibrary/models.py:94 msgid "file" msgstr "fichier" -#: content/file/models.py:26 +#: content/file/models.py:29 msgid "files" msgstr "fichiers" -#: content/image/models.py:45 content/image/models.py:54 +#: content/image/models.py:46 content/image/models.py:55 msgid "image" msgstr "image" -#: content/image/models.py:48 +#: content/image/models.py:49 msgid "alternate text" msgstr "" -#: content/image/models.py:49 +#: content/image/models.py:50 msgid "Description of image" msgstr "" -#: content/image/models.py:50 module/medialibrary/models.py:231 +#: content/image/models.py:51 module/medialibrary/models.py:260 msgid "caption" msgstr "légende" -#: content/image/models.py:55 +#: content/image/models.py:56 msgid "images" msgstr "images" -#: content/image/models.py:80 +#: content/image/models.py:82 msgid "position" msgstr "position" -#: content/image/models.py:88 +#: content/image/models.py:90 msgid "format" msgstr "" -#: content/medialibrary/models.py:49 content/section/models.py:36 -#: module/medialibrary/fields.py:59 module/medialibrary/models.py:97 +#: content/medialibrary/models.py:51 content/section/models.py:38 +#: module/medialibrary/fields.py:66 module/medialibrary/models.py:112 msgid "media file" msgstr "fichier de média" -#: content/medialibrary/models.py:50 module/medialibrary/models.py:98 +#: content/medialibrary/models.py:52 module/medialibrary/models.py:113 msgid "media files" msgstr "fichiers de média" -#: content/medialibrary/models.py:58 content/section/models.py:52 -#: content/table/models.py:82 +#: content/medialibrary/models.py:64 content/section/models.py:59 msgid "type" msgstr "type" -#: content/raw/models.py:18 +#: content/raw/models.py:20 msgid "raw content" msgstr "contenu cru" -#: content/raw/models.py:19 +#: content/raw/models.py:21 msgid "raw contents" msgstr "contenus crus" -#: content/richtext/models.py:22 +#: content/richtext/models.py:24 msgid "HTML Tidy" msgstr "" -#: content/richtext/models.py:23 +#: content/richtext/models.py:25 msgid "Ignore the HTML validation warnings" msgstr "" -#: content/richtext/models.py:47 +#: content/richtext/models.py:55 #, python-format msgid "" "HTML validation produced %(count)d warnings. Please review the updated " "content below before continuing: %(messages)s" msgstr "" -#: content/richtext/models.py:84 content/section/models.py:35 +#: content/richtext/models.py:99 content/section/models.py:36 msgid "text" msgstr "texte" -#: content/richtext/models.py:88 +#: content/richtext/models.py:103 msgid "rich text" msgstr "texte" -#: content/richtext/models.py:89 +#: content/richtext/models.py:104 msgid "rich texts" msgstr "textes" -#: content/rss/models.py:21 +#: content/rss/models.py:25 msgid "" "The rss field is updated several times a day. A change in the title will " "only be visible on the home page after the next feed update." -msgstr "Le champ rss est mis à jour plusieurs fois par jour. Un changement dans le titre ne sera visible que sur la page d'accueil après la mise à jour prochaine." +msgstr "" +"Le champ rss est mis à jour plusieurs fois par jour. Un changement dans le " +"titre ne sera visible que sur la page d'accueil après la mise à jour " +"prochaine." -#: content/rss/models.py:22 +#: content/rss/models.py:28 msgid "link" msgstr "lien" -#: content/rss/models.py:23 +#: content/rss/models.py:30 msgid "pre-rendered content" msgstr "contenu généré" -#: content/rss/models.py:24 +#: content/rss/models.py:32 msgid "last updated" msgstr "mise à jour" -#: content/rss/models.py:25 +#: content/rss/models.py:33 msgid "max. items" msgstr "max. éléments" -#: content/rss/models.py:29 +#: content/rss/models.py:37 msgid "RSS feed" msgstr "Fil RSS" -#: content/rss/models.py:30 +#: content/rss/models.py:38 msgid "RSS feeds" msgstr "Fils RSS" -#: content/section/models.py:41 +#: content/section/models.py:43 msgid "section" msgstr "section" -#: content/section/models.py:42 +#: content/section/models.py:44 msgid "sections" msgstr "sections" -#: content/table/models.py:63 -msgid "plain" -msgstr "plaine" - -#: content/table/models.py:64 -msgid "title row" -msgstr "titre de la ligne" - -#: content/table/models.py:66 -msgid "title row and column" -msgstr "ligne de titre et de la colonne" - -#: content/table/models.py:72 -msgid "table" -msgstr "tableau" - -#: content/table/models.py:73 -msgid "tables" -msgstr "tableaux" - -#: content/table/models.py:87 -msgid "data" -msgstr "données" - -#: content/template/models.py:51 +#: content/template/models.py:53 msgid "template content" msgstr "" -#: content/template/models.py:52 +#: content/template/models.py:54 msgid "template contents" msgstr "" -#: content/video/models.py:31 +#: content/template/models.py:63 models.py:400 +msgid "template" +msgstr "modèle" + +#: content/video/models.py:34 msgid "video link" msgstr "lien du vidéo" -#: content/video/models.py:32 +#: content/video/models.py:36 msgid "" -"This should be a link to a youtube or vimeo video, i.e.: " -"http://www.youtube.com/watch?v=zmj1rpzDRZ0" -msgstr "Cela devrait être un lien vers une vidéo YouTube ou Vimeo, à savoir: http://www.youtube.com/watch?v=zmj1rpzDRZ0" +"This should be a link to a youtube or vimeo video, i.e.: http://www.youtube." +"com/watch?v=zmj1rpzDRZ0" +msgstr "" +"Cela devrait être un lien vers une vidéo YouTube ou Vimeo, à savoir: http://" +"www.youtube.com/watch?v=zmj1rpzDRZ0" -#: content/video/models.py:37 +#: content/video/models.py:41 msgid "video" msgstr "vidéo" -#: content/video/models.py:38 +#: content/video/models.py:42 msgid "videos" msgstr "vidéos" -#: contrib/tagging.py:117 +#: contrib/tagging.py:132 msgid "Tagging" msgstr "" -#: module/blog/models.py:28 +#: models.py:550 +msgid "ordering" +msgstr "séquence" + +#: module/blog/extensions/tags.py:17 +msgid "tags" +msgstr "mots-clé" + +#: module/blog/extensions/translations.py:25 +#: module/extensions/translations.py:140 translations.py:282 +msgid "language" +msgstr "langue" + +#: module/blog/extensions/translations.py:35 +#: module/extensions/translations.py:148 +msgid "translation of" +msgstr "traduction de" + +#: module/blog/extensions/translations.py:39 +#: module/extensions/translations.py:152 +msgid "Leave this empty for entries in the primary language." +msgstr "" + +#: module/blog/extensions/translations.py:67 +#: templates/admin/feincms/item_editor.html:33 +msgid "available translations" +msgstr "traductions disponibles" + +#: module/blog/models.py:33 msgid "published" msgstr "publié" -#: module/blog/models.py:30 +#: module/blog/models.py:36 msgid "This is used for the generated navigation too." msgstr "Il est utilisé pour la navigation généré aussi." -#: module/blog/models.py:33 +#: module/blog/models.py:40 msgid "published on" msgstr "publié le" -#: module/blog/models.py:34 -msgid "" -"Will be set automatically once you tick the `published` checkbox above." -msgstr "Sera mis automatiquement une fois que vous cochez la case «publié» ci-dessus." +#: module/blog/models.py:42 +msgid "Will be set automatically once you tick the `published` checkbox above." +msgstr "" +"Sera mis automatiquement une fois que vous cochez la case «publié» ci-dessus." -#: module/blog/models.py:39 +#: module/blog/models.py:48 msgid "entry" msgstr "entrée" -#: module/blog/models.py:40 +#: module/blog/models.py:49 msgid "entries" msgstr "entrées" -#: module/blog/extensions/tags.py:12 -msgid "tags" -msgstr "mots-clé" - -#: module/blog/extensions/translations.py:20 -#: module/extensions/translations.py:126 -msgid "translation of" -msgstr "traduction de" - -#: module/blog/extensions/translations.py:23 -#: module/extensions/translations.py:129 -msgid "Leave this empty for entries in the primary language." -msgstr "" - -#: module/blog/extensions/translations.py:44 -#: templates/admin/feincms/item_editor.html:43 -msgid "available translations" -msgstr "traductions disponibles" - -#: module/extensions/changedate.py:32 +#: module/extensions/changedate.py:41 msgid "creation date" msgstr "date de création" -#: module/extensions/changedate.py:33 +#: module/extensions/changedate.py:43 msgid "modification date" msgstr "date de modification" -#: module/extensions/ct_tracker.py:140 +#: module/extensions/ct_tracker.py:156 msgid "content types" msgstr "types de contenu" -#: module/extensions/datepublisher.py:67 +#: module/extensions/datepublisher.py:86 msgid "publication date" msgstr "date de la publication" -#: module/extensions/datepublisher.py:70 +#: module/extensions/datepublisher.py:90 msgid "publication end date" msgstr "date de termination de la publication" -#: module/extensions/datepublisher.py:72 +#: module/extensions/datepublisher.py:93 msgid "Leave empty if the entry should stay active forever." msgstr "Laissez vide si l'entrée doit rester active pour toujours." -#: module/extensions/datepublisher.py:103 +#: module/extensions/datepublisher.py:128 msgid "visible from - to" msgstr "visible de - à" -#: module/extensions/datepublisher.py:113 +#: module/extensions/datepublisher.py:139 msgid "Date-based publishing" msgstr "" -#: module/extensions/featured.py:9 +#: module/extensions/featured.py:15 msgid "featured" msgstr "" -#: module/extensions/featured.py:14 +#: module/extensions/featured.py:21 msgid "Featured" msgstr "" -#: module/extensions/seo.py:9 +#: module/extensions/seo.py:16 msgid "meta keywords" msgstr "termes meta" -#: module/extensions/seo.py:10 -msgid "This will be prepended to the default keyword list." -msgstr "Ce sera ajouté à la liste de mots clés par défaut." +#: module/extensions/seo.py:18 +msgid "Keywords are ignored by most search engines." +msgstr "" -#: module/extensions/seo.py:11 +#: module/extensions/seo.py:20 msgid "meta description" msgstr "description meta" -#: module/extensions/seo.py:12 -msgid "This will be prepended to the default description." -msgstr "Ce sera ajouté à la description par défaut." +#: module/extensions/seo.py:22 +msgid "" +"This text is displayed on the search results page. It is however not used " +"for the SEO ranking. Text longer than 140 characters is truncated." +msgstr "" -#: module/extensions/seo.py:17 +#: module/extensions/seo.py:32 msgid "Search engine optimization" msgstr "" -#: module/extensions/translations.py:207 +#: module/extensions/translations.py:249 msgid "Edit translation" msgstr "Modifier la traduction" -#: module/extensions/translations.py:210 +#: module/extensions/translations.py:256 msgid "Create translation" msgstr "Créer traduction" -#: module/extensions/translations.py:215 +#: module/extensions/translations.py:264 msgid "translations" msgstr "traductions" -#: module/medialibrary/forms.py:26 +#: module/medialibrary/forms.py:31 msgid "This would create a loop in the hierarchy" msgstr "" -#: module/medialibrary/forms.py:64 +#: module/medialibrary/forms.py:77 #, python-format msgid "" "Cannot overwrite with different file type (attempt to overwrite a " "%(old_ext)s with a %(new_ext)s)" msgstr "" -#: module/medialibrary/modeladmins.py:58 +#: module/medialibrary/modeladmins.py:63 #, python-format msgid "Successfully added %(count)d media file to %(category)s." msgid_plural "Successfully added %(count)d media files to %(category)s." msgstr[0] "" msgstr[1] "" -#: module/medialibrary/modeladmins.py:77 +#: module/medialibrary/modeladmins.py:84 msgid "Add selected media files to category" msgstr "" -#: module/medialibrary/modeladmins.py:86 +#: module/medialibrary/modeladmins.py:94 #, python-format msgid "ZIP file exported as %s" msgstr "" -#: module/medialibrary/modeladmins.py:88 +#: module/medialibrary/modeladmins.py:96 #, python-format msgid "ZIP file export failed: %s" msgstr "" -#: module/medialibrary/modeladmins.py:93 +#: module/medialibrary/modeladmins.py:104 msgid "Export selected media files as zip file" msgstr "" -#: module/medialibrary/modeladmins.py:136 +#: module/medialibrary/modeladmins.py:157 #: templates/admin/feincms/page/page/item_editor.html:15 msgid "Preview" msgstr "Prévisualiser" -#: module/medialibrary/modeladmins.py:141 module/medialibrary/models.py:88 +#: module/medialibrary/modeladmins.py:162 module/medialibrary/models.py:103 msgid "file size" msgstr "taille" -#: module/medialibrary/modeladmins.py:146 module/medialibrary/models.py:86 +#: module/medialibrary/modeladmins.py:167 module/medialibrary/models.py:100 msgid "created" msgstr "crée" -#: module/medialibrary/modeladmins.py:165 module/medialibrary/models.py:85 +#: module/medialibrary/modeladmins.py:187 module/medialibrary/models.py:97 msgid "file type" msgstr "type de fichier" -#: module/medialibrary/modeladmins.py:186 +#: module/medialibrary/modeladmins.py:211 msgid "file info" msgstr "" -#: module/medialibrary/modeladmins.py:198 +#: module/medialibrary/modeladmins.py:224 #, python-format msgid "%d files imported" msgstr "" -#: module/medialibrary/modeladmins.py:200 +#: module/medialibrary/modeladmins.py:226 #, python-format msgid "ZIP import failed: %s" msgstr "" -#: module/medialibrary/modeladmins.py:202 +#: module/medialibrary/modeladmins.py:228 msgid "No input file given" msgstr "" -#: module/medialibrary/models.py:43 +#: module/medialibrary/models.py:49 msgid "parent" msgstr "parent" -#: module/medialibrary/models.py:45 module/page/models.py:171 +#: module/medialibrary/models.py:51 module/page/models.py:175 msgid "slug" msgstr "télougou" -#: module/medialibrary/models.py:49 +#: module/medialibrary/models.py:55 msgid "category" msgstr "catégorie" -#: module/medialibrary/models.py:50 module/medialibrary/models.py:90 +#: module/medialibrary/models.py:56 module/medialibrary/models.py:106 msgid "categories" msgstr "catégories" -#: module/medialibrary/models.py:87 +#: module/medialibrary/models.py:101 msgid "copyright" msgstr "copyright" -#: module/medialibrary/models.py:202 +#: module/medialibrary/models.py:219 msgid "Image" msgstr "image" -#: module/medialibrary/models.py:203 +#: module/medialibrary/models.py:221 msgid "Video" msgstr "" -#: module/medialibrary/models.py:204 +#: module/medialibrary/models.py:224 msgid "Audio" msgstr "" -#: module/medialibrary/models.py:205 +#: module/medialibrary/models.py:226 msgid "PDF document" msgstr "Document PDF" -#: module/medialibrary/models.py:206 +#: module/medialibrary/models.py:227 msgid "Flash" msgstr "Flash" -#: module/medialibrary/models.py:207 +#: module/medialibrary/models.py:228 msgid "Text" msgstr "texte" -#: module/medialibrary/models.py:208 +#: module/medialibrary/models.py:229 msgid "Rich Text" msgstr "" -#: module/medialibrary/models.py:209 +#: module/medialibrary/models.py:230 msgid "Zip archive" msgstr "" -#: module/medialibrary/models.py:210 +#: module/medialibrary/models.py:231 msgid "Microsoft Word" msgstr "" -#: module/medialibrary/models.py:211 +#: module/medialibrary/models.py:233 msgid "Microsoft Excel" msgstr "" -#: module/medialibrary/models.py:212 +#: module/medialibrary/models.py:235 msgid "Microsoft PowerPoint" msgstr "" -#: module/medialibrary/models.py:213 +#: module/medialibrary/models.py:237 msgid "Binary" msgstr "Données binaires" -#: module/medialibrary/models.py:232 +#: module/medialibrary/models.py:261 msgid "description" msgstr "description" -#: module/medialibrary/models.py:235 +#: module/medialibrary/models.py:264 msgid "media file translation" msgstr "traductions du fichier de média" -#: module/medialibrary/models.py:236 +#: module/medialibrary/models.py:265 msgid "media file translations" msgstr "traductions des fichiers de média" -#: module/page/forms.py:172 +#: module/page/extensions/excerpt.py:18 +msgid "excerpt" +msgstr "" + +#: module/page/extensions/excerpt.py:21 +msgid "Add a brief excerpt summarizing the content of this page." +msgstr "" + +#: module/page/extensions/excerpt.py:25 +msgid "Excerpt" +msgstr "" + +#: module/page/extensions/navigation.py:86 +#: module/page/extensions/navigation.py:115 +msgid "navigation extension" +msgstr "additif de la navigation" + +#: module/page/extensions/navigation.py:119 +msgid "" +"Select the module providing subpages for this page if you need to customize " +"the navigation." +msgstr "" +"Sélectionnez le module fournissant pages pour cette page si vous avez besoin " +"de personnaliser la navigation." + +#: module/page/extensions/navigation.py:134 +msgid "Navigation extension" +msgstr "" + +#: module/page/extensions/navigationgroups.py:20 +msgid "Default" +msgstr "" + +#: module/page/extensions/navigationgroups.py:21 +msgid "Footer" +msgstr "" + +#: module/page/extensions/navigationgroups.py:28 +#, fuzzy +#| msgid "in navigation" +msgid "navigation group" +msgstr "à la navigation" + +#: module/page/extensions/relatedpages.py:21 +msgid "Select pages that should be listed as related content." +msgstr "" + +#: module/page/extensions/relatedpages.py:26 +msgid "Related pages" +msgstr "" + +#: module/page/extensions/sites.py:21 +msgid "Site" +msgstr "" + +#: module/page/extensions/symlinks.py:22 +msgid "symlinked page" +msgstr "" + +#: module/page/extensions/symlinks.py:23 +msgid "All content is inherited from this page if given." +msgstr "Tout le contenu est hérité de cette page si donnée." + +#: module/page/extensions/titles.py:19 +msgid "content title" +msgstr "titre du contenu" + +#: module/page/extensions/titles.py:22 +msgid "The first line is the main title, the following lines are subtitles." +msgstr "" +"La première ligne est le titre principal, les lignes suivantes sont des sous-" +"titres." + +#: module/page/extensions/titles.py:26 +msgid "page title" +msgstr "titre de la page" + +#: module/page/extensions/titles.py:30 +#, fuzzy +#| msgid "Page title for browser window. Same as title by default." +msgid "" +"Page title for browser window. Same as title bydefault. Must not be longer " +"than 70 characters." +msgstr "" +"Titre de la page pour fenêtre de navigateur. Même que le titre par défaut." + +#: module/page/extensions/titles.py:60 +msgid "Titles" +msgstr "" + +#: module/page/forms.py:187 msgid "This URL is already taken by an active page." msgstr "Cette URL est déjà prise par une page active." -#: module/page/forms.py:190 +#: module/page/forms.py:206 msgid "This URL is already taken by another active page." msgstr "Cette URL est déjà pris par une autre page active." -#: module/page/modeladmins.py:41 +#: module/page/forms.py:211 +msgid "This page does not allow attachment of child pages" +msgstr "" + +#: module/page/modeladmins.py:42 msgid "Other options" msgstr "Autres options" -#: module/page/modeladmins.py:90 module/page/models.py:174 +#: module/page/modeladmins.py:80 module/page/models.py:182 msgid "in navigation" msgstr "à la navigation" -#: module/page/modeladmins.py:105 +#: module/page/modeladmins.py:109 module/page/modeladmins.py:111 msgid "Add child page" msgstr "Ajouter page enfant" -#: module/page/modeladmins.py:107 +#: module/page/modeladmins.py:120 module/page/modeladmins.py:122 #: templates/admin/feincms/content_inline.html:9 msgid "View on site" msgstr "Voir sur le site" -#: module/page/modeladmins.py:126 +#: module/page/modeladmins.py:142 #, python-format -msgid "Add %(language)s Translation of \"%(page)s\"" +msgid "Add %(language)s translation of \"%(page)s\"" msgstr "" -#: module/page/modeladmins.py:156 +#: module/page/modeladmins.py:177 msgid "" "The content from the original translation has been copied to the newly " "created page." msgstr "" -#: module/page/modeladmins.py:170 +#: module/page/modeladmins.py:197 msgid "You don't have the necessary permissions to edit this object" msgstr "" -#: module/page/modeladmins.py:185 +#: module/page/modeladmins.py:223 msgid "inherited" msgstr "hérité" -#: module/page/modeladmins.py:189 +#: module/page/modeladmins.py:229 msgid "extensions" msgstr "extensions" -#: module/page/modeladmins.py:193 module/page/models.py:206 +#: module/page/modeladmins.py:233 module/page/models.py:221 msgid "is active" msgstr "" -#: module/page/models.py:167 +#: module/page/models.py:169 msgid "active" msgstr "actif" -#: module/page/models.py:175 +#: module/page/models.py:173 +#, fuzzy +#| msgid "This is used for the generated navigation too." +msgid "This title is also used for navigation menu items." +msgstr "Il est utilisé pour la navigation généré aussi." + +#: module/page/models.py:176 +msgid "This is used to build the URL for this page" +msgstr "" + +#: module/page/models.py:184 msgid "override URL" msgstr "adresse URL forcée" -#: module/page/models.py:176 +#: module/page/models.py:186 msgid "" "Override the target URL. Be sure to include slashes at the beginning and at " -"the end if it is a local URL. This affects both the navigation and subpages'" -" URLs." -msgstr "Outrepasser l'URL cible. N'oubliez pas d'inclure des barres obliques au début et à la fin s'il s'agit d'une URL locale. Cela affecte la navigation et les adresses URL des sous-pages." +"the end if it is a local URL. This affects both the navigation and subpages' " +"URLs." +msgstr "" +"Outrepasser l'URL cible. N'oubliez pas d'inclure des barres obliques au " +"début et à la fin s'il s'agit d'une URL locale. Cela affecte la navigation " +"et les adresses URL des sous-pages." -#: module/page/models.py:177 +#: module/page/models.py:190 msgid "redirect to" msgstr "rediriger à" -#: module/page/models.py:178 +#: module/page/models.py:193 msgid "Target URL for automatic redirects or the primary key of a page." msgstr "" -#: module/page/models.py:180 +#: module/page/models.py:196 msgid "Cached URL" msgstr "adresse URL temporairement enregistrée" -#: module/page/models.py:395 +#: module/page/models.py:298 +#, python-format +msgid "" +"This %(page_class)s uses a singleton template, and " +"FEINCMS_SINGLETON_TEMPLATE_DELETION_ALLOWED=False" +msgstr "" + +#: module/page/models.py:426 msgid "page" msgstr "page" -#: module/page/models.py:396 +#: module/page/models.py:427 msgid "pages" msgstr "pages" -#: module/page/extensions/excerpt.py:9 -msgid "excerpt" -msgstr "" - -#: module/page/extensions/excerpt.py:10 -msgid "Add a brief excerpt summarizing the content of this page." -msgstr "" - -#: module/page/extensions/excerpt.py:12 -msgid "Excerpt" -msgstr "" - -#: module/page/extensions/navigation.py:82 -#: module/page/extensions/navigation.py:107 -msgid "navigation extension" -msgstr "additif de la navigation" - -#: module/page/extensions/navigation.py:110 -msgid "" -"Select the module providing subpages for this page if you need to customize " -"the navigation." -msgstr "Sélectionnez le module fournissant pages pour cette page si vous avez besoin de personnaliser la navigation." - -#: module/page/extensions/navigation.py:125 -msgid "Navigation extension" -msgstr "" - -#: module/page/extensions/relatedpages.py:14 -msgid "Select pages that should be listed as related content." -msgstr "" - -#: module/page/extensions/relatedpages.py:19 -msgid "Related pages" -msgstr "" - -#: module/page/extensions/sites.py:16 -msgid "Site" -msgstr "" - -#: module/page/extensions/symlinks.py:15 -msgid "symlinked page" -msgstr "" - -#: module/page/extensions/symlinks.py:16 -msgid "All content is inherited from this page if given." -msgstr "Tout le contenu est hérité de cette page si donnée." - -#: module/page/extensions/titles.py:13 -msgid "content title" -msgstr "titre du contenu" - -#: module/page/extensions/titles.py:14 -msgid "The first line is the main title, the following lines are subtitles." -msgstr "La première ligne est le titre principal, les lignes suivantes sont des sous-titres." - -#: module/page/extensions/titles.py:15 -msgid "page title" -msgstr "titre de la page" - -#: module/page/extensions/titles.py:16 -msgid "Page title for browser window. Same as title by default." -msgstr "Titre de la page pour fenêtre de navigateur. Même que le titre par défaut." - -#: module/page/extensions/titles.py:43 -msgid "Titles" -msgstr "" - -#: templates/admin/filter.html:3 -#, python-format -msgid " By %(filter_title)s " -msgstr "Par %(filter_title)s " - #: templates/admin/feincms/_messages_js.html:4 msgid "Really delete item?" msgstr "Vraiment supprimer cet élément?" @@ -756,7 +789,9 @@ msgstr "Impossible de supprimer cet élément" #: templates/admin/feincms/_messages_js.html:6 msgid "Cannot delete item, because it is parent of at least one other item." -msgstr "Impossible de supprimer l'élément, parce qu'il est le parent d'au moins un autre élément." +msgstr "" +"Impossible de supprimer l'élément, parce qu'il est le parent d'au moins un " +"autre élément." #: templates/admin/feincms/_messages_js.html:7 msgid "Change template" @@ -764,14 +799,15 @@ msgstr "Changer modèle" #: templates/admin/feincms/_messages_js.html:8 msgid "Really change template?
    All changes are saved." -msgstr "Réellement changer de modèle?
    Toutes les modifications sont enregistrées." +msgstr "" +"Réellement changer de modèle?
    Toutes les modifications sont " +"enregistrées." #: templates/admin/feincms/_messages_js.html:9 #, python-format msgid "" -"Really change template?
    All changes are saved and content from " -"%%(source_regions)s is moved to " -"%%(target_region)s." +"Really change template?
    All changes are saved and content from " +"%%(source_regions)s is moved to %%(target_region)s." msgstr "" #: templates/admin/feincms/_messages_js.html:12 @@ -806,22 +842,24 @@ msgstr "Région vide" msgid "" "Content from the parent site is automatically inherited. To override this " "behaviour, add some content." -msgstr "Contenu du site parent est automatiquement hérité. Pour contourner ce comportement, ajoutez un peu de contenu." +msgstr "" +"Contenu du site parent est automatiquement hérité. Pour contourner ce " +"comportement, ajoutez un peu de contenu." #: templates/admin/feincms/content_editor.html:33 msgid "Add new item" msgstr "Ajouter un autre" -#: templates/admin/feincms/content_inline.html:91 +#: templates/admin/feincms/content_inline.html:93 #, python-format msgid "Add another %(verbose_name)s" msgstr "" -#: templates/admin/feincms/content_inline.html:94 +#: templates/admin/feincms/content_inline.html:96 msgid "Remove" msgstr "" -#: templates/admin/feincms/fe_editor.html:40 +#: templates/admin/feincms/fe_editor.html:30 msgid "Save" msgstr "Enregistrer" @@ -849,13 +887,21 @@ msgstr "vers le bas" msgid "remove" msgstr "supprimer" -#: templates/admin/feincms/recover_form.html:8 -#: templates/admin/feincms/revision_form.html:8 +#: templates/admin/feincms/page/page/item_editor.html:10 +msgid "Edit on site" +msgstr "" + #: templates/admin/feincms/page/page/item_editor.html:23 #: templates/admin/feincms/page/page/tree_editor.html:7 +#: templates/admin/feincms/recover_form.html:8 +#: templates/admin/feincms/revision_form.html:8 msgid "Home" msgstr "Page d'accueil" +#: templates/admin/feincms/page/page/item_editor.html:27 +msgid "Add" +msgstr "" + #: templates/admin/feincms/recover_form.html:11 #, python-format msgid "Recover deleted %(verbose_name)s" @@ -878,29 +924,26 @@ msgstr "" msgid "Press the save button below to revert to this version of the object." msgstr "" -#: templates/admin/feincms/tree_editor.html:32 +#: templates/admin/feincms/tree_editor.html:22 msgid "Shortcuts" msgstr "Raccourcis" -#: templates/admin/feincms/tree_editor.html:34 +#: templates/admin/feincms/tree_editor.html:24 msgid "Collapse tree" msgstr "Réduire l'arbre" -#: templates/admin/feincms/tree_editor.html:35 +#: templates/admin/feincms/tree_editor.html:25 msgid "Expand tree" msgstr "Développer l'arborescence" -#: templates/admin/feincms/tree_editor.html:38 +#: templates/admin/feincms/tree_editor.html:29 msgid "Filter" msgstr "Filtre" -#: templates/admin/feincms/page/page/item_editor.html:10 -msgid "Edit on site" -msgstr "" - -#: templates/admin/feincms/page/page/item_editor.html:27 -msgid "Add" -msgstr "" +#: templates/admin/filter.html:3 +#, python-format +msgid " By %(filter_title)s " +msgstr "Par %(filter_title)s " #: templates/admin/medialibrary/add_to_category.html:5 #: templates/admin/medialibrary/add_to_category.html:9 @@ -944,7 +987,8 @@ msgstr "" #, python-format msgid "" "\n" -" %(comment_username)s said on %(comment_submit_date)s
    \n" +" %(comment_username)s said on %(comment_submit_date)s
    \n" " " msgstr "" @@ -963,3 +1007,27 @@ msgstr "Envoyer" #: templates/content/contactform/thanks.html:3 msgid "Thanks!" msgstr "Merci!" + +#~ msgid "plain" +#~ msgstr "plaine" + +#~ msgid "title row" +#~ msgstr "titre de la ligne" + +#~ msgid "title row and column" +#~ msgstr "ligne de titre et de la colonne" + +#~ msgid "table" +#~ msgstr "tableau" + +#~ msgid "tables" +#~ msgstr "tableaux" + +#~ msgid "data" +#~ msgstr "données" + +#~ msgid "This will be prepended to the default keyword list." +#~ msgstr "Ce sera ajouté à la liste de mots clés par défaut." + +#~ msgid "This will be prepended to the default description." +#~ msgstr "Ce sera ajouté à la description par défaut." diff --git a/feincms/locale/hr/LC_MESSAGES/django.po b/feincms/locale/hr/LC_MESSAGES/django.po index 0a562024c..2ef4eef95 100644 --- a/feincms/locale/hr/LC_MESSAGES/django.po +++ b/feincms/locale/hr/LC_MESSAGES/django.po @@ -1,446 +1,439 @@ # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. -# +# # Translators: # Bojan Mihelač , 2010 msgid "" msgstr "" "Project-Id-Version: FeinCMS\n" -"Report-Msgid-Bugs-To: http://github.com/feincms/feincms/issues\n" -"POT-Creation-Date: 2013-10-25 11:12+0200\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2014-07-20 14:59+0200\n" "PO-Revision-Date: 2013-10-25 09:15+0000\n" "Last-Translator: Matthias Kestenholz \n" -"Language-Team: Croatian (http://www.transifex.com/projects/p/feincms/language/hr/)\n" +"Language-Team: Croatian (http://www.transifex.com/projects/p/feincms/" +"language/hr/)\n" +"Language: hr\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Language: hr\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" - -#: models.py:370 content/template/models.py:59 -msgid "template" -msgstr "predložak" - -#: models.py:510 -msgid "ordering" -msgstr "poredak" +"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" -#: translations.py:259 module/blog/extensions/translations.py:17 -#: module/extensions/translations.py:123 -msgid "language" -msgstr "jezik" - -#: admin/filterspecs.py:35 admin/filterspecs.py:70 +#: admin/filterspecs.py:47 admin/filterspecs.py:86 msgid "All" msgstr "Svi" -#: admin/filterspecs.py:46 module/page/models.py:172 +#: admin/filterspecs.py:58 module/page/models.py:178 msgid "Parent" msgstr "Nadređeni" -#: admin/filterspecs.py:81 +#: admin/filterspecs.py:97 #: templates/admin/medialibrary/mediafile/change_list.html:14 msgid "Category" msgstr "Kategorija" -#: admin/item_editor.py:159 +#: admin/item_editor.py:190 #, python-format msgid "Change %s" msgstr "Promjeni %s" -#: admin/tree_editor.py:234 content/rss/models.py:20 -#: content/section/models.py:34 module/blog/models.py:29 -#: module/medialibrary/models.py:40 module/page/models.py:170 -#: module/page/models.py:226 +#: admin/tree_editor.py:294 content/rss/models.py:23 +#: content/section/models.py:35 module/blog/models.py:35 +#: module/medialibrary/models.py:45 module/page/models.py:172 +#: module/page/models.py:240 msgid "title" msgstr "naziv" -#: admin/tree_editor.py:407 +#: admin/tree_editor.py:353 admin/tree_editor.py:370 +msgid "You do not have permission to modify this object" +msgstr "" + +#: admin/tree_editor.py:491 +msgid "No permission" +msgstr "" + +#: admin/tree_editor.py:509 #, python-format msgid "%s has been moved to a new position." msgstr "%s je premještena na novu poziciju." -#: admin/tree_editor.py:411 +#: admin/tree_editor.py:512 msgid "Did not understand moving instruction." msgstr "Uputa za premještanje nije uspješno interpretirana. " -#: admin/tree_editor.py:420 +#: admin/tree_editor.py:523 msgid "actions" msgstr "akcije" -#: admin/tree_editor.py:432 +#: admin/tree_editor.py:552 #, python-format -msgid "Successfully deleted %s items." +msgid "Successfully deleted %(count)d items." msgstr "" -#: admin/tree_editor.py:440 +#: admin/tree_editor.py:565 #, python-format msgid "Delete selected %(verbose_name_plural)s" msgstr "" -#: content/application/models.py:218 +#: content/application/models.py:147 msgid "application content" msgstr "sadržaj aplikacije" -#: content/application/models.py:219 +#: content/application/models.py:148 msgid "application contents" msgstr "sadržaji aplikacije" -#: content/application/models.py:249 +#: content/application/models.py:179 msgid "application" msgstr "aplikacija" -#: content/comments/models.py:24 +#: content/comments/models.py:32 msgid "enabled" msgstr "omogućeno" -#: content/comments/models.py:24 +#: content/comments/models.py:33 msgid "New comments may be added" msgstr "Novi komentari mogu biti dodani" -#: content/comments/models.py:28 content/comments/models.py:29 +#: content/comments/models.py:37 content/comments/models.py:38 msgid "comments" msgstr "komentari" -#: content/comments/models.py:49 +#: content/comments/models.py:60 msgid "public" msgstr "objavljeno" -#: content/comments/models.py:49 +#: content/comments/models.py:61 msgid "not public" msgstr "neobjavljeno" -#: content/contactform/models.py:18 +#: content/contactform/models.py:20 msgid "name" msgstr "ime" -#: content/contactform/models.py:19 +#: content/contactform/models.py:21 msgid "email" msgstr "email" -#: content/contactform/models.py:20 +#: content/contactform/models.py:22 msgid "subject" msgstr "naslov" -#: content/contactform/models.py:23 content/raw/models.py:14 +#: content/contactform/models.py:26 content/raw/models.py:16 msgid "content" msgstr "sadržaj" -#: content/contactform/models.py:34 +#: content/contactform/models.py:37 msgid "contact form" msgstr "kontaktni obrazac" -#: content/contactform/models.py:35 +#: content/contactform/models.py:38 msgid "contact forms" msgstr "kontaktni obrazci" -#: content/file/models.py:20 content/file/models.py:25 -#: module/medialibrary/models.py:84 +#: content/file/models.py:23 content/file/models.py:28 +#: module/medialibrary/models.py:94 msgid "file" msgstr "datoteka" -#: content/file/models.py:26 +#: content/file/models.py:29 msgid "files" msgstr "datoteke" -#: content/image/models.py:45 content/image/models.py:54 +#: content/image/models.py:46 content/image/models.py:55 msgid "image" msgstr "slika" -#: content/image/models.py:48 +#: content/image/models.py:49 msgid "alternate text" msgstr "" -#: content/image/models.py:49 +#: content/image/models.py:50 msgid "Description of image" msgstr "" -#: content/image/models.py:50 module/medialibrary/models.py:231 +#: content/image/models.py:51 module/medialibrary/models.py:260 msgid "caption" msgstr "naslov" -#: content/image/models.py:55 +#: content/image/models.py:56 msgid "images" msgstr "slike" -#: content/image/models.py:80 +#: content/image/models.py:82 msgid "position" msgstr "pozicija" -#: content/image/models.py:88 +#: content/image/models.py:90 msgid "format" msgstr "" -#: content/medialibrary/models.py:49 content/section/models.py:36 -#: module/medialibrary/fields.py:59 module/medialibrary/models.py:97 +#: content/medialibrary/models.py:51 content/section/models.py:38 +#: module/medialibrary/fields.py:66 module/medialibrary/models.py:112 msgid "media file" msgstr "medijska datoteka" -#: content/medialibrary/models.py:50 module/medialibrary/models.py:98 +#: content/medialibrary/models.py:52 module/medialibrary/models.py:113 msgid "media files" msgstr "medijske datoteke" -#: content/medialibrary/models.py:58 content/section/models.py:52 -#: content/table/models.py:82 +#: content/medialibrary/models.py:64 content/section/models.py:59 msgid "type" msgstr "tip" -#: content/raw/models.py:18 +#: content/raw/models.py:20 msgid "raw content" msgstr "neformatirani sadržaj" -#: content/raw/models.py:19 +#: content/raw/models.py:21 msgid "raw contents" msgstr "neformatirani sadržaji" -#: content/richtext/models.py:22 +#: content/richtext/models.py:24 msgid "HTML Tidy" msgstr "HTML Tidy" -#: content/richtext/models.py:23 +#: content/richtext/models.py:25 msgid "Ignore the HTML validation warnings" msgstr "Zanemariti upozorenja HTML provjere" -#: content/richtext/models.py:47 +#: content/richtext/models.py:55 #, python-format msgid "" "HTML validation produced %(count)d warnings. Please review the updated " "content below before continuing: %(messages)s" -msgstr "HTML provjera je pokazala %(count)d upozorenja. Molimo pregledajte ažurirani sadržaj ispod prije nastavka: %(messages)s" +msgstr "" +"HTML provjera je pokazala %(count)d upozorenja. Molimo pregledajte ažurirani " +"sadržaj ispod prije nastavka: %(messages)s" -#: content/richtext/models.py:84 content/section/models.py:35 +#: content/richtext/models.py:99 content/section/models.py:36 msgid "text" msgstr "tekst" -#: content/richtext/models.py:88 +#: content/richtext/models.py:103 msgid "rich text" msgstr "formatirani tekst" -#: content/richtext/models.py:89 +#: content/richtext/models.py:104 msgid "rich texts" msgstr "formatirani tekstovi" -#: content/rss/models.py:21 +#: content/rss/models.py:25 msgid "" "The rss field is updated several times a day. A change in the title will " "only be visible on the home page after the next feed update." -msgstr "RSS polje se ažurira nekoliko puta dnevno. Promjena u naslovu će biti vidljiva na naslovnici nakon sljedećeg ažuriranja kanala." +msgstr "" +"RSS polje se ažurira nekoliko puta dnevno. Promjena u naslovu će biti " +"vidljiva na naslovnici nakon sljedećeg ažuriranja kanala." -#: content/rss/models.py:22 +#: content/rss/models.py:28 msgid "link" msgstr "link" -#: content/rss/models.py:23 +#: content/rss/models.py:30 msgid "pre-rendered content" msgstr "pred pripremljen sadržaj" -#: content/rss/models.py:24 +#: content/rss/models.py:32 msgid "last updated" msgstr "zadnji put osvježeno" -#: content/rss/models.py:25 +#: content/rss/models.py:33 msgid "max. items" msgstr "najviše zapisa" -#: content/rss/models.py:29 +#: content/rss/models.py:37 msgid "RSS feed" msgstr "RSS kanal" -#: content/rss/models.py:30 +#: content/rss/models.py:38 msgid "RSS feeds" msgstr "RSS kanali" -#: content/section/models.py:41 +#: content/section/models.py:43 msgid "section" msgstr "sekcija" -#: content/section/models.py:42 +#: content/section/models.py:44 msgid "sections" msgstr "sekcije" -#: content/table/models.py:63 -msgid "plain" -msgstr "jednostavno" - -#: content/table/models.py:64 -msgid "title row" -msgstr "naslovni redak" - -#: content/table/models.py:66 -msgid "title row and column" -msgstr "naslovni redak i kolona" - -#: content/table/models.py:72 -msgid "table" -msgstr "tablica" - -#: content/table/models.py:73 -msgid "tables" -msgstr "tablice" - -#: content/table/models.py:87 -msgid "data" -msgstr "podaci" - -#: content/template/models.py:51 +#: content/template/models.py:53 msgid "template content" msgstr "sadržaj predloška" -#: content/template/models.py:52 +#: content/template/models.py:54 msgid "template contents" msgstr "sadržaji predloška" -#: content/video/models.py:31 +#: content/template/models.py:63 models.py:400 +msgid "template" +msgstr "predložak" + +#: content/video/models.py:34 msgid "video link" msgstr "link na video" -#: content/video/models.py:32 +#: content/video/models.py:36 msgid "" -"This should be a link to a youtube or vimeo video, i.e.: " -"http://www.youtube.com/watch?v=zmj1rpzDRZ0" -msgstr "Polje treba sadržavati link na youtube ili vimeo video, i.e.: http://www.youtube.com/watch?v=zmj1rpzDRZ0" +"This should be a link to a youtube or vimeo video, i.e.: http://www.youtube." +"com/watch?v=zmj1rpzDRZ0" +msgstr "" +"Polje treba sadržavati link na youtube ili vimeo video, i.e.: http://www." +"youtube.com/watch?v=zmj1rpzDRZ0" -#: content/video/models.py:37 +#: content/video/models.py:41 msgid "video" msgstr "video" -#: content/video/models.py:38 +#: content/video/models.py:42 msgid "videos" msgstr "video zapisi" -#: contrib/tagging.py:117 +#: contrib/tagging.py:132 msgid "Tagging" msgstr "" -#: module/blog/models.py:28 +#: models.py:550 +msgid "ordering" +msgstr "poredak" + +#: module/blog/extensions/tags.py:17 +msgid "tags" +msgstr "etikete" + +#: module/blog/extensions/translations.py:25 +#: module/extensions/translations.py:140 translations.py:282 +msgid "language" +msgstr "jezik" + +#: module/blog/extensions/translations.py:35 +#: module/extensions/translations.py:148 +msgid "translation of" +msgstr "prijevod od" + +#: module/blog/extensions/translations.py:39 +#: module/extensions/translations.py:152 +msgid "Leave this empty for entries in the primary language." +msgstr "Ostavite ovo prazno za zapise u primarnom jeziku." + +#: module/blog/extensions/translations.py:67 +#: templates/admin/feincms/item_editor.html:33 +msgid "available translations" +msgstr "dostupni prijevodi" + +#: module/blog/models.py:33 msgid "published" msgstr "objavljeno" -#: module/blog/models.py:30 +#: module/blog/models.py:36 msgid "This is used for the generated navigation too." msgstr "Ovo se koristi i za generiranu navigaciju." -#: module/blog/models.py:33 +#: module/blog/models.py:40 msgid "published on" msgstr "objavljeno na" -#: module/blog/models.py:34 -msgid "" -"Will be set automatically once you tick the `published` checkbox above." +#: module/blog/models.py:42 +msgid "Will be set automatically once you tick the `published` checkbox above." msgstr "Biti će automatski postavljeno kada označite `objavljeno` polje iznad." -#: module/blog/models.py:39 +#: module/blog/models.py:48 msgid "entry" msgstr "članak" -#: module/blog/models.py:40 +#: module/blog/models.py:49 msgid "entries" msgstr "članci" -#: module/blog/extensions/tags.py:12 -msgid "tags" -msgstr "etikete" - -#: module/blog/extensions/translations.py:20 -#: module/extensions/translations.py:126 -msgid "translation of" -msgstr "prijevod od" - -#: module/blog/extensions/translations.py:23 -#: module/extensions/translations.py:129 -msgid "Leave this empty for entries in the primary language." -msgstr "Ostavite ovo prazno za zapise u primarnom jeziku." - -#: module/blog/extensions/translations.py:44 -#: templates/admin/feincms/item_editor.html:43 -msgid "available translations" -msgstr "dostupni prijevodi" - -#: module/extensions/changedate.py:32 +#: module/extensions/changedate.py:41 msgid "creation date" msgstr "datum kreiranja" -#: module/extensions/changedate.py:33 +#: module/extensions/changedate.py:43 msgid "modification date" msgstr "datum izmjene" -#: module/extensions/ct_tracker.py:140 +#: module/extensions/ct_tracker.py:156 msgid "content types" msgstr "tip sadržaja" -#: module/extensions/datepublisher.py:67 +#: module/extensions/datepublisher.py:86 msgid "publication date" msgstr "datum objave" -#: module/extensions/datepublisher.py:70 +#: module/extensions/datepublisher.py:90 msgid "publication end date" msgstr "datum kraja objave" -#: module/extensions/datepublisher.py:72 +#: module/extensions/datepublisher.py:93 msgid "Leave empty if the entry should stay active forever." -msgstr "Ostavite praznim ukoliko članak treba biti aktivan neograničeno vrijeme." +msgstr "" +"Ostavite praznim ukoliko članak treba biti aktivan neograničeno vrijeme." -#: module/extensions/datepublisher.py:103 +#: module/extensions/datepublisher.py:128 msgid "visible from - to" msgstr "vidljiv od - do" -#: module/extensions/datepublisher.py:113 +#: module/extensions/datepublisher.py:139 msgid "Date-based publishing" msgstr "Objava vezana na datum" -#: module/extensions/featured.py:9 +#: module/extensions/featured.py:15 msgid "featured" msgstr "istaknuti" -#: module/extensions/featured.py:14 +#: module/extensions/featured.py:21 msgid "Featured" msgstr "Istaknuti" -#: module/extensions/seo.py:9 +#: module/extensions/seo.py:16 msgid "meta keywords" msgstr "meta ključne riječi" -#: module/extensions/seo.py:10 -msgid "This will be prepended to the default keyword list." -msgstr "Ovo će biti dodano predefiniranoj listi ključnih riječi." +#: module/extensions/seo.py:18 +msgid "Keywords are ignored by most search engines." +msgstr "" -#: module/extensions/seo.py:11 +#: module/extensions/seo.py:20 msgid "meta description" msgstr "meta opis" -#: module/extensions/seo.py:12 -msgid "This will be prepended to the default description." -msgstr "Ovo će biti dodano predefiniranom opisu." +#: module/extensions/seo.py:22 +msgid "" +"This text is displayed on the search results page. It is however not used " +"for the SEO ranking. Text longer than 140 characters is truncated." +msgstr "" -#: module/extensions/seo.py:17 +#: module/extensions/seo.py:32 msgid "Search engine optimization" msgstr "Optimizacija za tražilice" -#: module/extensions/translations.py:207 +#: module/extensions/translations.py:249 msgid "Edit translation" msgstr "Uredi prijevod" -#: module/extensions/translations.py:210 +#: module/extensions/translations.py:256 msgid "Create translation" msgstr "Kreiraj prijevod" -#: module/extensions/translations.py:215 +#: module/extensions/translations.py:264 msgid "translations" msgstr "prijevodi" -#: module/medialibrary/forms.py:26 +#: module/medialibrary/forms.py:31 msgid "This would create a loop in the hierarchy" msgstr "" -#: module/medialibrary/forms.py:64 +#: module/medialibrary/forms.py:77 #, python-format msgid "" "Cannot overwrite with different file type (attempt to overwrite a " "%(old_ext)s with a %(new_ext)s)" msgstr "" -#: module/medialibrary/modeladmins.py:58 +#: module/medialibrary/modeladmins.py:63 #, python-format msgid "Successfully added %(count)d media file to %(category)s." msgid_plural "Successfully added %(count)d media files to %(category)s." @@ -448,298 +441,339 @@ msgstr[0] "" msgstr[1] "" msgstr[2] "" -#: module/medialibrary/modeladmins.py:77 +#: module/medialibrary/modeladmins.py:84 msgid "Add selected media files to category" msgstr "" -#: module/medialibrary/modeladmins.py:86 +#: module/medialibrary/modeladmins.py:94 #, python-format msgid "ZIP file exported as %s" msgstr "" -#: module/medialibrary/modeladmins.py:88 +#: module/medialibrary/modeladmins.py:96 #, python-format msgid "ZIP file export failed: %s" msgstr "" -#: module/medialibrary/modeladmins.py:93 +#: module/medialibrary/modeladmins.py:104 msgid "Export selected media files as zip file" msgstr "" -#: module/medialibrary/modeladmins.py:136 +#: module/medialibrary/modeladmins.py:157 #: templates/admin/feincms/page/page/item_editor.html:15 msgid "Preview" msgstr "Pregled" -#: module/medialibrary/modeladmins.py:141 module/medialibrary/models.py:88 +#: module/medialibrary/modeladmins.py:162 module/medialibrary/models.py:103 msgid "file size" msgstr "veličina datoteke" -#: module/medialibrary/modeladmins.py:146 module/medialibrary/models.py:86 +#: module/medialibrary/modeladmins.py:167 module/medialibrary/models.py:100 msgid "created" msgstr "kreiran" -#: module/medialibrary/modeladmins.py:165 module/medialibrary/models.py:85 +#: module/medialibrary/modeladmins.py:187 module/medialibrary/models.py:97 msgid "file type" msgstr "tip datoteke" -#: module/medialibrary/modeladmins.py:186 +#: module/medialibrary/modeladmins.py:211 msgid "file info" msgstr "informacije o datoteci" -#: module/medialibrary/modeladmins.py:198 +#: module/medialibrary/modeladmins.py:224 #, python-format msgid "%d files imported" msgstr "" -#: module/medialibrary/modeladmins.py:200 +#: module/medialibrary/modeladmins.py:226 #, python-format msgid "ZIP import failed: %s" msgstr "" -#: module/medialibrary/modeladmins.py:202 +#: module/medialibrary/modeladmins.py:228 msgid "No input file given" msgstr "" -#: module/medialibrary/models.py:43 +#: module/medialibrary/models.py:49 msgid "parent" msgstr "nadređeni" -#: module/medialibrary/models.py:45 module/page/models.py:171 +#: module/medialibrary/models.py:51 module/page/models.py:175 msgid "slug" msgstr "slug" -#: module/medialibrary/models.py:49 +#: module/medialibrary/models.py:55 msgid "category" msgstr "kategorija" -#: module/medialibrary/models.py:50 module/medialibrary/models.py:90 +#: module/medialibrary/models.py:56 module/medialibrary/models.py:106 msgid "categories" msgstr "kategorije" -#: module/medialibrary/models.py:87 +#: module/medialibrary/models.py:101 msgid "copyright" msgstr "autorsko pravo" -#: module/medialibrary/models.py:202 +#: module/medialibrary/models.py:219 msgid "Image" msgstr "Slika" -#: module/medialibrary/models.py:203 +#: module/medialibrary/models.py:221 msgid "Video" msgstr "Video" -#: module/medialibrary/models.py:204 +#: module/medialibrary/models.py:224 msgid "Audio" msgstr "Audio" -#: module/medialibrary/models.py:205 +#: module/medialibrary/models.py:226 msgid "PDF document" msgstr "PDF dokument" -#: module/medialibrary/models.py:206 +#: module/medialibrary/models.py:227 msgid "Flash" msgstr "Flash" -#: module/medialibrary/models.py:207 +#: module/medialibrary/models.py:228 msgid "Text" msgstr "Tekst" -#: module/medialibrary/models.py:208 +#: module/medialibrary/models.py:229 msgid "Rich Text" msgstr "Formatirani tekst" -#: module/medialibrary/models.py:209 +#: module/medialibrary/models.py:230 msgid "Zip archive" msgstr "" -#: module/medialibrary/models.py:210 +#: module/medialibrary/models.py:231 msgid "Microsoft Word" msgstr "Microsoft Word" -#: module/medialibrary/models.py:211 +#: module/medialibrary/models.py:233 msgid "Microsoft Excel" msgstr "Microsoft Excel" -#: module/medialibrary/models.py:212 +#: module/medialibrary/models.py:235 msgid "Microsoft PowerPoint" msgstr "Microsoft PowerPoint" -#: module/medialibrary/models.py:213 +#: module/medialibrary/models.py:237 msgid "Binary" msgstr "Binarni" -#: module/medialibrary/models.py:232 +#: module/medialibrary/models.py:261 msgid "description" msgstr "opis" -#: module/medialibrary/models.py:235 +#: module/medialibrary/models.py:264 msgid "media file translation" msgstr "prijevod medijske datoteke" -#: module/medialibrary/models.py:236 +#: module/medialibrary/models.py:265 msgid "media file translations" msgstr "prijevodi medijske datoteke" -#: module/page/forms.py:172 +#: module/page/extensions/excerpt.py:18 +msgid "excerpt" +msgstr "sažetak" + +#: module/page/extensions/excerpt.py:21 +msgid "Add a brief excerpt summarizing the content of this page." +msgstr "Dodajte kratak sažetak sadržaja ove stranice." + +#: module/page/extensions/excerpt.py:25 +msgid "Excerpt" +msgstr "Sažetak" + +#: module/page/extensions/navigation.py:86 +#: module/page/extensions/navigation.py:115 +msgid "navigation extension" +msgstr "navigacijska ekstenzija" + +#: module/page/extensions/navigation.py:119 +msgid "" +"Select the module providing subpages for this page if you need to customize " +"the navigation." +msgstr "" +"Odaberite modul koji će dostaviti podstranice za ovu stranicu ukoliko je " +"potrebno prilagoditi navigaciju." + +#: module/page/extensions/navigation.py:134 +msgid "Navigation extension" +msgstr "navigacijska ekstenzija" + +#: module/page/extensions/navigationgroups.py:20 +msgid "Default" +msgstr "" + +#: module/page/extensions/navigationgroups.py:21 +msgid "Footer" +msgstr "" + +#: module/page/extensions/navigationgroups.py:28 +#, fuzzy +#| msgid "in navigation" +msgid "navigation group" +msgstr "u navigaciji" + +#: module/page/extensions/relatedpages.py:21 +msgid "Select pages that should be listed as related content." +msgstr "Odaberite stranice koje trebaju biti navedene kao povezani sadržaj." + +#: module/page/extensions/relatedpages.py:26 +msgid "Related pages" +msgstr "Povezane stranice" + +#: module/page/extensions/sites.py:21 +msgid "Site" +msgstr "" + +#: module/page/extensions/symlinks.py:22 +msgid "symlinked page" +msgstr "simbolički povezana stranica" + +#: module/page/extensions/symlinks.py:23 +msgid "All content is inherited from this page if given." +msgstr "Sav sadržaj je nasljeđen od ove stranice ukoliko je postavljena." + +#: module/page/extensions/titles.py:19 +msgid "content title" +msgstr "naslov sadržaja" + +#: module/page/extensions/titles.py:22 +msgid "The first line is the main title, the following lines are subtitles." +msgstr "Prva linija je glavni naslov, slijedeće linije su podnaslovi." + +#: module/page/extensions/titles.py:26 +msgid "page title" +msgstr "naslov stranice" + +#: module/page/extensions/titles.py:30 +#, fuzzy +#| msgid "Page title for browser window. Same as title by default." +msgid "" +"Page title for browser window. Same as title bydefault. Must not be longer " +"than 70 characters." +msgstr "" +"Naslov stranice u prozoru browsera. Podrazumijevana vrijednost je jednako " +"kao naslov." + +#: module/page/extensions/titles.py:60 +msgid "Titles" +msgstr "Nazivi" + +#: module/page/forms.py:187 msgid "This URL is already taken by an active page." msgstr "Ova adresa (URL) je već zauzeta aktivnom stranicom." -#: module/page/forms.py:190 +#: module/page/forms.py:206 msgid "This URL is already taken by another active page." msgstr "Ova adresa (URL) je već zauzeta drugom aktivnom stranicom." -#: module/page/modeladmins.py:41 +#: module/page/forms.py:211 +msgid "This page does not allow attachment of child pages" +msgstr "" + +#: module/page/modeladmins.py:42 msgid "Other options" msgstr "Druge mogućnosti" -#: module/page/modeladmins.py:90 module/page/models.py:174 +#: module/page/modeladmins.py:80 module/page/models.py:182 msgid "in navigation" msgstr "u navigaciji" -#: module/page/modeladmins.py:105 +#: module/page/modeladmins.py:109 module/page/modeladmins.py:111 msgid "Add child page" msgstr "Dodaj podređenu stranicu" -#: module/page/modeladmins.py:107 +#: module/page/modeladmins.py:120 module/page/modeladmins.py:122 #: templates/admin/feincms/content_inline.html:9 msgid "View on site" msgstr "Pogledaj na internetnim stranicama" -#: module/page/modeladmins.py:126 +#: module/page/modeladmins.py:142 #, python-format -msgid "Add %(language)s Translation of \"%(page)s\"" +msgid "Add %(language)s translation of \"%(page)s\"" msgstr "" -#: module/page/modeladmins.py:156 +#: module/page/modeladmins.py:177 msgid "" "The content from the original translation has been copied to the newly " "created page." msgstr "" -#: module/page/modeladmins.py:170 +#: module/page/modeladmins.py:197 msgid "You don't have the necessary permissions to edit this object" msgstr "" -#: module/page/modeladmins.py:185 +#: module/page/modeladmins.py:223 msgid "inherited" msgstr "naslijeđeno" -#: module/page/modeladmins.py:189 +#: module/page/modeladmins.py:229 msgid "extensions" msgstr "ekstenzije" -#: module/page/modeladmins.py:193 module/page/models.py:206 +#: module/page/modeladmins.py:233 module/page/models.py:221 msgid "is active" msgstr "je aktivna" -#: module/page/models.py:167 +#: module/page/models.py:169 msgid "active" msgstr "aktivan" -#: module/page/models.py:175 +#: module/page/models.py:173 +#, fuzzy +#| msgid "This is used for the generated navigation too." +msgid "This title is also used for navigation menu items." +msgstr "Ovo se koristi i za generiranu navigaciju." + +#: module/page/models.py:176 +msgid "This is used to build the URL for this page" +msgstr "" + +#: module/page/models.py:184 msgid "override URL" msgstr "nadjačati URL" -#: module/page/models.py:176 +#: module/page/models.py:186 msgid "" "Override the target URL. Be sure to include slashes at the beginning and at " -"the end if it is a local URL. This affects both the navigation and subpages'" -" URLs." -msgstr "Nadjačati ciljnu adresu (URL). Budite sigurni da uključite kose crte na početku i kraju ukoliko se odnosi na lokalnu adresu. Ovo se odnosi i na navigaciju i na adrese podstranica" +"the end if it is a local URL. This affects both the navigation and subpages' " +"URLs." +msgstr "" +"Nadjačati ciljnu adresu (URL). Budite sigurni da uključite kose crte na " +"početku i kraju ukoliko se odnosi na lokalnu adresu. Ovo se odnosi i na " +"navigaciju i na adrese podstranica" -#: module/page/models.py:177 +#: module/page/models.py:190 msgid "redirect to" msgstr "preusmjeriti na" -#: module/page/models.py:178 +#: module/page/models.py:193 msgid "Target URL for automatic redirects or the primary key of a page." msgstr "" -#: module/page/models.py:180 +#: module/page/models.py:196 msgid "Cached URL" msgstr "Keširana adresa (URL)" -#: module/page/models.py:395 +#: module/page/models.py:298 +#, python-format +msgid "" +"This %(page_class)s uses a singleton template, and " +"FEINCMS_SINGLETON_TEMPLATE_DELETION_ALLOWED=False" +msgstr "" + +#: module/page/models.py:426 msgid "page" msgstr "stranica" -#: module/page/models.py:396 +#: module/page/models.py:427 msgid "pages" msgstr "stranice" -#: module/page/extensions/excerpt.py:9 -msgid "excerpt" -msgstr "sažetak" - -#: module/page/extensions/excerpt.py:10 -msgid "Add a brief excerpt summarizing the content of this page." -msgstr "Dodajte kratak sažetak sadržaja ove stranice." - -#: module/page/extensions/excerpt.py:12 -msgid "Excerpt" -msgstr "Sažetak" - -#: module/page/extensions/navigation.py:82 -#: module/page/extensions/navigation.py:107 -msgid "navigation extension" -msgstr "navigacijska ekstenzija" - -#: module/page/extensions/navigation.py:110 -msgid "" -"Select the module providing subpages for this page if you need to customize " -"the navigation." -msgstr "Odaberite modul koji će dostaviti podstranice za ovu stranicu ukoliko je potrebno prilagoditi navigaciju." - -#: module/page/extensions/navigation.py:125 -msgid "Navigation extension" -msgstr "navigacijska ekstenzija" - -#: module/page/extensions/relatedpages.py:14 -msgid "Select pages that should be listed as related content." -msgstr "Odaberite stranice koje trebaju biti navedene kao povezani sadržaj." - -#: module/page/extensions/relatedpages.py:19 -msgid "Related pages" -msgstr "Povezane stranice" - -#: module/page/extensions/sites.py:16 -msgid "Site" -msgstr "" - -#: module/page/extensions/symlinks.py:15 -msgid "symlinked page" -msgstr "simbolički povezana stranica" - -#: module/page/extensions/symlinks.py:16 -msgid "All content is inherited from this page if given." -msgstr "Sav sadržaj je nasljeđen od ove stranice ukoliko je postavljena." - -#: module/page/extensions/titles.py:13 -msgid "content title" -msgstr "naslov sadržaja" - -#: module/page/extensions/titles.py:14 -msgid "The first line is the main title, the following lines are subtitles." -msgstr "Prva linija je glavni naslov, slijedeće linije su podnaslovi." - -#: module/page/extensions/titles.py:15 -msgid "page title" -msgstr "naslov stranice" - -#: module/page/extensions/titles.py:16 -msgid "Page title for browser window. Same as title by default." -msgstr "Naslov stranice u prozoru browsera. Podrazumijevana vrijednost je jednako kao naslov." - -#: module/page/extensions/titles.py:43 -msgid "Titles" -msgstr "Nazivi" - -#: templates/admin/filter.html:3 -#, python-format -msgid " By %(filter_title)s " -msgstr " Po %(filter_title)s " - #: templates/admin/feincms/_messages_js.html:4 msgid "Really delete item?" msgstr "Stvarno izbrisati zapis?" @@ -758,7 +792,8 @@ msgstr "Zapis ne može biti obrisan" #: templates/admin/feincms/_messages_js.html:6 msgid "Cannot delete item, because it is parent of at least one other item." -msgstr "Zapis ne može biti obrisan jer je nadređeni od najmanje jednog drugog zapisa." +msgstr "" +"Zapis ne može biti obrisan jer je nadređeni od najmanje jednog drugog zapisa." #: templates/admin/feincms/_messages_js.html:7 msgid "Change template" @@ -771,9 +806,8 @@ msgstr "Zaista zamijeniti predložak?
    Sve izmjene će biti spremljene." #: templates/admin/feincms/_messages_js.html:9 #, python-format msgid "" -"Really change template?
    All changes are saved and content from " -"%%(source_regions)s is moved to " -"%%(target_region)s." +"Really change template?
    All changes are saved and content from " +"%%(source_regions)s is moved to %%(target_region)s." msgstr "" #: templates/admin/feincms/_messages_js.html:12 @@ -808,22 +842,24 @@ msgstr "Regija je prazna" msgid "" "Content from the parent site is automatically inherited. To override this " "behaviour, add some content." -msgstr "Sadržaj je automatski naslijeđen od nadređene stranice. Ukoliko želite to promjeniti, dodajte neke sadržaje." +msgstr "" +"Sadržaj je automatski naslijeđen od nadređene stranice. Ukoliko želite to " +"promjeniti, dodajte neke sadržaje." #: templates/admin/feincms/content_editor.html:33 msgid "Add new item" msgstr "Dodaj novi unos" -#: templates/admin/feincms/content_inline.html:91 +#: templates/admin/feincms/content_inline.html:93 #, python-format msgid "Add another %(verbose_name)s" msgstr "" -#: templates/admin/feincms/content_inline.html:94 +#: templates/admin/feincms/content_inline.html:96 msgid "Remove" msgstr "" -#: templates/admin/feincms/fe_editor.html:40 +#: templates/admin/feincms/fe_editor.html:30 msgid "Save" msgstr "Spremi" @@ -851,13 +887,21 @@ msgstr "dolje" msgid "remove" msgstr "izbriši" -#: templates/admin/feincms/recover_form.html:8 -#: templates/admin/feincms/revision_form.html:8 +#: templates/admin/feincms/page/page/item_editor.html:10 +msgid "Edit on site" +msgstr "Uredi na stranicama" + #: templates/admin/feincms/page/page/item_editor.html:23 #: templates/admin/feincms/page/page/tree_editor.html:7 +#: templates/admin/feincms/recover_form.html:8 +#: templates/admin/feincms/revision_form.html:8 msgid "Home" msgstr "Home" +#: templates/admin/feincms/page/page/item_editor.html:27 +msgid "Add" +msgstr "" + #: templates/admin/feincms/recover_form.html:11 #, python-format msgid "Recover deleted %(verbose_name)s" @@ -880,29 +924,26 @@ msgstr "" msgid "Press the save button below to revert to this version of the object." msgstr "" -#: templates/admin/feincms/tree_editor.html:32 +#: templates/admin/feincms/tree_editor.html:22 msgid "Shortcuts" msgstr "Kratice" -#: templates/admin/feincms/tree_editor.html:34 +#: templates/admin/feincms/tree_editor.html:24 msgid "Collapse tree" msgstr "Sažmi drvo" -#: templates/admin/feincms/tree_editor.html:35 +#: templates/admin/feincms/tree_editor.html:25 msgid "Expand tree" msgstr "Proširi drvo" -#: templates/admin/feincms/tree_editor.html:38 +#: templates/admin/feincms/tree_editor.html:29 msgid "Filter" msgstr "Filter" -#: templates/admin/feincms/page/page/item_editor.html:10 -msgid "Edit on site" -msgstr "Uredi na stranicama" - -#: templates/admin/feincms/page/page/item_editor.html:27 -msgid "Add" -msgstr "" +#: templates/admin/filter.html:3 +#, python-format +msgid " By %(filter_title)s " +msgstr " Po %(filter_title)s " #: templates/admin/medialibrary/add_to_category.html:5 #: templates/admin/medialibrary/add_to_category.html:9 @@ -946,9 +987,14 @@ msgstr "%(comment_count)s komentara." #, python-format msgid "" "\n" -" %(comment_username)s said on %(comment_submit_date)s
    \n" +" %(comment_username)s said on %(comment_submit_date)s
    \n" +" " +msgstr "" +"\n" +" %(comment_username)s je objavio " +"%(comment_submit_date)s
    \n" " " -msgstr "\n %(comment_username)s je objavio %(comment_submit_date)s
    \n " #: templates/content/comments/default.html:28 msgid "No comments." @@ -965,3 +1011,27 @@ msgstr "Potvrdi" #: templates/content/contactform/thanks.html:3 msgid "Thanks!" msgstr "Hvala!" + +#~ msgid "plain" +#~ msgstr "jednostavno" + +#~ msgid "title row" +#~ msgstr "naslovni redak" + +#~ msgid "title row and column" +#~ msgstr "naslovni redak i kolona" + +#~ msgid "table" +#~ msgstr "tablica" + +#~ msgid "tables" +#~ msgstr "tablice" + +#~ msgid "data" +#~ msgstr "podaci" + +#~ msgid "This will be prepended to the default keyword list." +#~ msgstr "Ovo će biti dodano predefiniranoj listi ključnih riječi." + +#~ msgid "This will be prepended to the default description." +#~ msgstr "Ovo će biti dodano predefiniranom opisu." diff --git a/feincms/locale/it/LC_MESSAGES/django.po b/feincms/locale/it/LC_MESSAGES/django.po index 71688d404..f76e0ec25 100644 --- a/feincms/locale/it/LC_MESSAGES/django.po +++ b/feincms/locale/it/LC_MESSAGES/django.po @@ -1,743 +1,776 @@ # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. -# +# # Translators: msgid "" msgstr "" "Project-Id-Version: FeinCMS\n" -"Report-Msgid-Bugs-To: http://github.com/feincms/feincms/issues\n" -"POT-Creation-Date: 2013-10-25 11:12+0200\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2014-07-20 14:59+0200\n" "PO-Revision-Date: 2013-10-25 09:15+0000\n" "Last-Translator: Matthias Kestenholz \n" -"Language-Team: Italian (http://www.transifex.com/projects/p/feincms/language/it/)\n" +"Language-Team: Italian (http://www.transifex.com/projects/p/feincms/language/" +"it/)\n" +"Language: it\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Language: it\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: models.py:370 content/template/models.py:59 -msgid "template" -msgstr "template" - -#: models.py:510 -msgid "ordering" -msgstr "ordinazione" - -#: translations.py:259 module/blog/extensions/translations.py:17 -#: module/extensions/translations.py:123 -msgid "language" -msgstr "lingua" - -#: admin/filterspecs.py:35 admin/filterspecs.py:70 +#: admin/filterspecs.py:47 admin/filterspecs.py:86 msgid "All" msgstr "Tutto" -#: admin/filterspecs.py:46 module/page/models.py:172 +#: admin/filterspecs.py:58 module/page/models.py:178 msgid "Parent" msgstr "Parent" -#: admin/filterspecs.py:81 +#: admin/filterspecs.py:97 #: templates/admin/medialibrary/mediafile/change_list.html:14 msgid "Category" msgstr "" -#: admin/item_editor.py:159 +#: admin/item_editor.py:190 #, python-format msgid "Change %s" msgstr "Variazione% s" -#: admin/tree_editor.py:234 content/rss/models.py:20 -#: content/section/models.py:34 module/blog/models.py:29 -#: module/medialibrary/models.py:40 module/page/models.py:170 -#: module/page/models.py:226 +#: admin/tree_editor.py:294 content/rss/models.py:23 +#: content/section/models.py:35 module/blog/models.py:35 +#: module/medialibrary/models.py:45 module/page/models.py:172 +#: module/page/models.py:240 msgid "title" msgstr "Titolo" -#: admin/tree_editor.py:407 +#: admin/tree_editor.py:353 admin/tree_editor.py:370 +msgid "You do not have permission to modify this object" +msgstr "" + +#: admin/tree_editor.py:491 +msgid "No permission" +msgstr "" + +#: admin/tree_editor.py:509 #, python-format msgid "%s has been moved to a new position." msgstr "" -#: admin/tree_editor.py:411 +#: admin/tree_editor.py:512 msgid "Did not understand moving instruction." msgstr "" -#: admin/tree_editor.py:420 +#: admin/tree_editor.py:523 msgid "actions" msgstr "azioni" -#: admin/tree_editor.py:432 +#: admin/tree_editor.py:552 #, python-format -msgid "Successfully deleted %s items." +msgid "Successfully deleted %(count)d items." msgstr "" -#: admin/tree_editor.py:440 +#: admin/tree_editor.py:565 #, python-format msgid "Delete selected %(verbose_name_plural)s" msgstr "" -#: content/application/models.py:218 +#: content/application/models.py:147 msgid "application content" msgstr "" -#: content/application/models.py:219 +#: content/application/models.py:148 msgid "application contents" msgstr "" -#: content/application/models.py:249 +#: content/application/models.py:179 msgid "application" msgstr "applicazione" -#: content/comments/models.py:24 +#: content/comments/models.py:32 msgid "enabled" msgstr "" -#: content/comments/models.py:24 +#: content/comments/models.py:33 msgid "New comments may be added" msgstr "" -#: content/comments/models.py:28 content/comments/models.py:29 +#: content/comments/models.py:37 content/comments/models.py:38 msgid "comments" msgstr "" -#: content/comments/models.py:49 +#: content/comments/models.py:60 msgid "public" msgstr "" -#: content/comments/models.py:49 +#: content/comments/models.py:61 msgid "not public" msgstr "" -#: content/contactform/models.py:18 +#: content/contactform/models.py:20 msgid "name" msgstr "nome" -#: content/contactform/models.py:19 +#: content/contactform/models.py:21 msgid "email" msgstr "e-mail" -#: content/contactform/models.py:20 +#: content/contactform/models.py:22 msgid "subject" msgstr "soggetto" -#: content/contactform/models.py:23 content/raw/models.py:14 +#: content/contactform/models.py:26 content/raw/models.py:16 msgid "content" msgstr "contenuto" -#: content/contactform/models.py:34 +#: content/contactform/models.py:37 msgid "contact form" msgstr "forme di contatto" -#: content/contactform/models.py:35 +#: content/contactform/models.py:38 msgid "contact forms" msgstr "forme di contatto" -#: content/file/models.py:20 content/file/models.py:25 -#: module/medialibrary/models.py:84 +#: content/file/models.py:23 content/file/models.py:28 +#: module/medialibrary/models.py:94 msgid "file" msgstr "file" -#: content/file/models.py:26 +#: content/file/models.py:29 msgid "files" msgstr "files" -#: content/image/models.py:45 content/image/models.py:54 +#: content/image/models.py:46 content/image/models.py:55 msgid "image" msgstr "image" -#: content/image/models.py:48 +#: content/image/models.py:49 msgid "alternate text" msgstr "" -#: content/image/models.py:49 +#: content/image/models.py:50 msgid "Description of image" msgstr "" -#: content/image/models.py:50 module/medialibrary/models.py:231 +#: content/image/models.py:51 module/medialibrary/models.py:260 msgid "caption" msgstr "caption" -#: content/image/models.py:55 +#: content/image/models.py:56 msgid "images" msgstr "immagini" -#: content/image/models.py:80 +#: content/image/models.py:82 msgid "position" msgstr "Posizione" -#: content/image/models.py:88 +#: content/image/models.py:90 msgid "format" msgstr "" -#: content/medialibrary/models.py:49 content/section/models.py:36 -#: module/medialibrary/fields.py:59 module/medialibrary/models.py:97 +#: content/medialibrary/models.py:51 content/section/models.py:38 +#: module/medialibrary/fields.py:66 module/medialibrary/models.py:112 msgid "media file" msgstr "file multimediale" -#: content/medialibrary/models.py:50 module/medialibrary/models.py:98 +#: content/medialibrary/models.py:52 module/medialibrary/models.py:113 msgid "media files" msgstr "file multimediali" -#: content/medialibrary/models.py:58 content/section/models.py:52 -#: content/table/models.py:82 +#: content/medialibrary/models.py:64 content/section/models.py:59 msgid "type" msgstr "tipo" -#: content/raw/models.py:18 +#: content/raw/models.py:20 msgid "raw content" msgstr "raw contenuti" -#: content/raw/models.py:19 +#: content/raw/models.py:21 msgid "raw contents" msgstr "raw contenuti" -#: content/richtext/models.py:22 +#: content/richtext/models.py:24 msgid "HTML Tidy" msgstr "" -#: content/richtext/models.py:23 +#: content/richtext/models.py:25 msgid "Ignore the HTML validation warnings" msgstr "" -#: content/richtext/models.py:47 +#: content/richtext/models.py:55 #, python-format msgid "" "HTML validation produced %(count)d warnings. Please review the updated " "content below before continuing: %(messages)s" msgstr "" -#: content/richtext/models.py:84 content/section/models.py:35 +#: content/richtext/models.py:99 content/section/models.py:36 msgid "text" msgstr "testo" -#: content/richtext/models.py:88 +#: content/richtext/models.py:103 msgid "rich text" msgstr "rich text" -#: content/richtext/models.py:89 +#: content/richtext/models.py:104 msgid "rich texts" msgstr "" -#: content/rss/models.py:21 +#: content/rss/models.py:25 msgid "" "The rss field is updated several times a day. A change in the title will " "only be visible on the home page after the next feed update." -msgstr "Il campo rss viene aggiornato più volte al giorno. Una modifica del titolo sarà visibile solo in home page dopo il prossimo aggiornamento dei mangimi." +msgstr "" +"Il campo rss viene aggiornato più volte al giorno. Una modifica del titolo " +"sarà visibile solo in home page dopo il prossimo aggiornamento dei mangimi." -#: content/rss/models.py:22 +#: content/rss/models.py:28 msgid "link" msgstr "collegamento" -#: content/rss/models.py:23 +#: content/rss/models.py:30 msgid "pre-rendered content" msgstr "pre-rendering di contenuti" -#: content/rss/models.py:24 +#: content/rss/models.py:32 msgid "last updated" msgstr "Ultimo aggiornamento" -#: content/rss/models.py:25 +#: content/rss/models.py:33 msgid "max. items" msgstr "max. articoli" -#: content/rss/models.py:29 +#: content/rss/models.py:37 msgid "RSS feed" msgstr "RSS feed" -#: content/rss/models.py:30 +#: content/rss/models.py:38 msgid "RSS feeds" msgstr "Feed RSS" -#: content/section/models.py:41 +#: content/section/models.py:43 msgid "section" msgstr "sezione" -#: content/section/models.py:42 +#: content/section/models.py:44 msgid "sections" msgstr "sezioni" -#: content/table/models.py:63 -msgid "plain" -msgstr "plain" - -#: content/table/models.py:64 -msgid "title row" -msgstr "titolo di fila" - -#: content/table/models.py:66 -msgid "title row and column" -msgstr "riga del titolo e colonna" - -#: content/table/models.py:72 -msgid "table" -msgstr "tavolo" - -#: content/table/models.py:73 -msgid "tables" -msgstr "tavoli" - -#: content/table/models.py:87 -msgid "data" -msgstr "dati" - -#: content/template/models.py:51 +#: content/template/models.py:53 msgid "template content" msgstr "" -#: content/template/models.py:52 +#: content/template/models.py:54 msgid "template contents" msgstr "" -#: content/video/models.py:31 +#: content/template/models.py:63 models.py:400 +msgid "template" +msgstr "template" + +#: content/video/models.py:34 msgid "video link" msgstr "video link" -#: content/video/models.py:32 +#: content/video/models.py:36 msgid "" -"This should be a link to a youtube or vimeo video, i.e.: " -"http://www.youtube.com/watch?v=zmj1rpzDRZ0" -msgstr "Questo dovrebbe essere un link ad un video di YouTube o di Vimeo, vale a dire: http://www.youtube.com/watch?v=zmj1rpzDRZ0" +"This should be a link to a youtube or vimeo video, i.e.: http://www.youtube." +"com/watch?v=zmj1rpzDRZ0" +msgstr "" +"Questo dovrebbe essere un link ad un video di YouTube o di Vimeo, vale a " +"dire: http://www.youtube.com/watch?v=zmj1rpzDRZ0" -#: content/video/models.py:37 +#: content/video/models.py:41 msgid "video" msgstr "video" -#: content/video/models.py:38 +#: content/video/models.py:42 msgid "videos" msgstr "video" -#: contrib/tagging.py:117 +#: contrib/tagging.py:132 msgid "Tagging" msgstr "" -#: module/blog/models.py:28 +#: models.py:550 +msgid "ordering" +msgstr "ordinazione" + +#: module/blog/extensions/tags.py:17 +msgid "tags" +msgstr "tags" + +#: module/blog/extensions/translations.py:25 +#: module/extensions/translations.py:140 translations.py:282 +msgid "language" +msgstr "lingua" + +#: module/blog/extensions/translations.py:35 +#: module/extensions/translations.py:148 +msgid "translation of" +msgstr "traduzione di" + +#: module/blog/extensions/translations.py:39 +#: module/extensions/translations.py:152 +msgid "Leave this empty for entries in the primary language." +msgstr "" + +#: module/blog/extensions/translations.py:67 +#: templates/admin/feincms/item_editor.html:33 +msgid "available translations" +msgstr "traduzioni disponibili" + +#: module/blog/models.py:33 msgid "published" msgstr "pubblicato" -#: module/blog/models.py:30 +#: module/blog/models.py:36 msgid "This is used for the generated navigation too." msgstr "Questo viene utilizzato per la navigazione generato troppo." -#: module/blog/models.py:33 +#: module/blog/models.py:40 msgid "published on" msgstr "pubblicato il" -#: module/blog/models.py:34 -msgid "" -"Will be set automatically once you tick the `published` checkbox above." -msgstr "Verrà impostato automaticamente una volta che barrare la casella di controllo `` pubblicato in precedenza." +#: module/blog/models.py:42 +msgid "Will be set automatically once you tick the `published` checkbox above." +msgstr "" +"Verrà impostato automaticamente una volta che barrare la casella di " +"controllo `` pubblicato in precedenza." -#: module/blog/models.py:39 +#: module/blog/models.py:48 msgid "entry" msgstr "" -#: module/blog/models.py:40 +#: module/blog/models.py:49 msgid "entries" msgstr "" -#: module/blog/extensions/tags.py:12 -msgid "tags" -msgstr "tags" - -#: module/blog/extensions/translations.py:20 -#: module/extensions/translations.py:126 -msgid "translation of" -msgstr "traduzione di" - -#: module/blog/extensions/translations.py:23 -#: module/extensions/translations.py:129 -msgid "Leave this empty for entries in the primary language." -msgstr "" - -#: module/blog/extensions/translations.py:44 -#: templates/admin/feincms/item_editor.html:43 -msgid "available translations" -msgstr "traduzioni disponibili" - -#: module/extensions/changedate.py:32 +#: module/extensions/changedate.py:41 msgid "creation date" msgstr "data di creazione" -#: module/extensions/changedate.py:33 +#: module/extensions/changedate.py:43 msgid "modification date" msgstr "" -#: module/extensions/ct_tracker.py:140 +#: module/extensions/ct_tracker.py:156 msgid "content types" msgstr "tipi di contenuto" -#: module/extensions/datepublisher.py:67 +#: module/extensions/datepublisher.py:86 msgid "publication date" msgstr "data di pubblicazione" -#: module/extensions/datepublisher.py:70 +#: module/extensions/datepublisher.py:90 msgid "publication end date" msgstr "data fine pubblicazione" -#: module/extensions/datepublisher.py:72 +#: module/extensions/datepublisher.py:93 msgid "Leave empty if the entry should stay active forever." msgstr "Lasciare vuoto se la voce dovrebbe rimanere attivo per sempre." -#: module/extensions/datepublisher.py:103 +#: module/extensions/datepublisher.py:128 msgid "visible from - to" msgstr "visibile da - a" -#: module/extensions/datepublisher.py:113 +#: module/extensions/datepublisher.py:139 msgid "Date-based publishing" msgstr "" -#: module/extensions/featured.py:9 +#: module/extensions/featured.py:15 msgid "featured" msgstr "" -#: module/extensions/featured.py:14 +#: module/extensions/featured.py:21 msgid "Featured" msgstr "" -#: module/extensions/seo.py:9 +#: module/extensions/seo.py:16 msgid "meta keywords" msgstr "meta keywords" -#: module/extensions/seo.py:10 -msgid "This will be prepended to the default keyword list." -msgstr "Questo sarà anteposto al elenco di parole chiave predefinite." +#: module/extensions/seo.py:18 +msgid "Keywords are ignored by most search engines." +msgstr "" -#: module/extensions/seo.py:11 +#: module/extensions/seo.py:20 msgid "meta description" msgstr "meta description" -#: module/extensions/seo.py:12 -msgid "This will be prepended to the default description." -msgstr "Questo sarà anteposto alla descrizione di default." +#: module/extensions/seo.py:22 +msgid "" +"This text is displayed on the search results page. It is however not used " +"for the SEO ranking. Text longer than 140 characters is truncated." +msgstr "" -#: module/extensions/seo.py:17 +#: module/extensions/seo.py:32 msgid "Search engine optimization" msgstr "" -#: module/extensions/translations.py:207 +#: module/extensions/translations.py:249 msgid "Edit translation" msgstr "Modificare la traduzione" -#: module/extensions/translations.py:210 +#: module/extensions/translations.py:256 msgid "Create translation" msgstr "Crea traduzione" -#: module/extensions/translations.py:215 +#: module/extensions/translations.py:264 msgid "translations" msgstr "traduzioni" -#: module/medialibrary/forms.py:26 +#: module/medialibrary/forms.py:31 msgid "This would create a loop in the hierarchy" msgstr "" -#: module/medialibrary/forms.py:64 +#: module/medialibrary/forms.py:77 #, python-format msgid "" "Cannot overwrite with different file type (attempt to overwrite a " "%(old_ext)s with a %(new_ext)s)" msgstr "" -#: module/medialibrary/modeladmins.py:58 +#: module/medialibrary/modeladmins.py:63 #, python-format msgid "Successfully added %(count)d media file to %(category)s." msgid_plural "Successfully added %(count)d media files to %(category)s." msgstr[0] "" msgstr[1] "" -#: module/medialibrary/modeladmins.py:77 +#: module/medialibrary/modeladmins.py:84 msgid "Add selected media files to category" msgstr "" -#: module/medialibrary/modeladmins.py:86 +#: module/medialibrary/modeladmins.py:94 #, python-format msgid "ZIP file exported as %s" msgstr "" -#: module/medialibrary/modeladmins.py:88 +#: module/medialibrary/modeladmins.py:96 #, python-format msgid "ZIP file export failed: %s" msgstr "" -#: module/medialibrary/modeladmins.py:93 +#: module/medialibrary/modeladmins.py:104 msgid "Export selected media files as zip file" msgstr "" -#: module/medialibrary/modeladmins.py:136 +#: module/medialibrary/modeladmins.py:157 #: templates/admin/feincms/page/page/item_editor.html:15 msgid "Preview" msgstr "Anteprima" -#: module/medialibrary/modeladmins.py:141 module/medialibrary/models.py:88 +#: module/medialibrary/modeladmins.py:162 module/medialibrary/models.py:103 msgid "file size" msgstr "dimensione del file" -#: module/medialibrary/modeladmins.py:146 module/medialibrary/models.py:86 +#: module/medialibrary/modeladmins.py:167 module/medialibrary/models.py:100 msgid "created" msgstr "creato" -#: module/medialibrary/modeladmins.py:165 module/medialibrary/models.py:85 +#: module/medialibrary/modeladmins.py:187 module/medialibrary/models.py:97 msgid "file type" msgstr "tipo di file" -#: module/medialibrary/modeladmins.py:186 +#: module/medialibrary/modeladmins.py:211 msgid "file info" msgstr "file info" -#: module/medialibrary/modeladmins.py:198 +#: module/medialibrary/modeladmins.py:224 #, python-format msgid "%d files imported" msgstr "" -#: module/medialibrary/modeladmins.py:200 +#: module/medialibrary/modeladmins.py:226 #, python-format msgid "ZIP import failed: %s" msgstr "" -#: module/medialibrary/modeladmins.py:202 +#: module/medialibrary/modeladmins.py:228 msgid "No input file given" msgstr "" -#: module/medialibrary/models.py:43 +#: module/medialibrary/models.py:49 msgid "parent" msgstr "genitore" -#: module/medialibrary/models.py:45 module/page/models.py:171 +#: module/medialibrary/models.py:51 module/page/models.py:175 msgid "slug" msgstr "slug" -#: module/medialibrary/models.py:49 +#: module/medialibrary/models.py:55 msgid "category" msgstr "categoria" -#: module/medialibrary/models.py:50 module/medialibrary/models.py:90 +#: module/medialibrary/models.py:56 module/medialibrary/models.py:106 msgid "categories" msgstr "categorie" -#: module/medialibrary/models.py:87 +#: module/medialibrary/models.py:101 msgid "copyright" msgstr "copyright" -#: module/medialibrary/models.py:202 +#: module/medialibrary/models.py:219 msgid "Image" msgstr "Image" -#: module/medialibrary/models.py:203 +#: module/medialibrary/models.py:221 msgid "Video" msgstr "" -#: module/medialibrary/models.py:204 +#: module/medialibrary/models.py:224 msgid "Audio" msgstr "" -#: module/medialibrary/models.py:205 +#: module/medialibrary/models.py:226 msgid "PDF document" msgstr "PDF document" -#: module/medialibrary/models.py:206 +#: module/medialibrary/models.py:227 msgid "Flash" msgstr "Flash" -#: module/medialibrary/models.py:207 +#: module/medialibrary/models.py:228 msgid "Text" msgstr "Testo" -#: module/medialibrary/models.py:208 +#: module/medialibrary/models.py:229 msgid "Rich Text" msgstr "" -#: module/medialibrary/models.py:209 +#: module/medialibrary/models.py:230 msgid "Zip archive" msgstr "" -#: module/medialibrary/models.py:210 +#: module/medialibrary/models.py:231 msgid "Microsoft Word" msgstr "" -#: module/medialibrary/models.py:211 +#: module/medialibrary/models.py:233 msgid "Microsoft Excel" msgstr "" -#: module/medialibrary/models.py:212 +#: module/medialibrary/models.py:235 msgid "Microsoft PowerPoint" msgstr "" -#: module/medialibrary/models.py:213 +#: module/medialibrary/models.py:237 msgid "Binary" msgstr "Binary" -#: module/medialibrary/models.py:232 +#: module/medialibrary/models.py:261 msgid "description" msgstr "descrizione" -#: module/medialibrary/models.py:235 +#: module/medialibrary/models.py:264 msgid "media file translation" msgstr "traduzione di file multimediale" -#: module/medialibrary/models.py:236 +#: module/medialibrary/models.py:265 msgid "media file translations" msgstr "traduzioni di file multimediale" -#: module/page/forms.py:172 +#: module/page/extensions/excerpt.py:18 +msgid "excerpt" +msgstr "" + +#: module/page/extensions/excerpt.py:21 +msgid "Add a brief excerpt summarizing the content of this page." +msgstr "" + +#: module/page/extensions/excerpt.py:25 +msgid "Excerpt" +msgstr "" + +#: module/page/extensions/navigation.py:86 +#: module/page/extensions/navigation.py:115 +msgid "navigation extension" +msgstr "" + +#: module/page/extensions/navigation.py:119 +msgid "" +"Select the module providing subpages for this page if you need to customize " +"the navigation." +msgstr "" +"Selezionare il modulo che fornisce sottopagine per questa pagina, se avete " +"bisogno di personalizzare la navigazione." + +#: module/page/extensions/navigation.py:134 +msgid "Navigation extension" +msgstr "" + +#: module/page/extensions/navigationgroups.py:20 +msgid "Default" +msgstr "" + +#: module/page/extensions/navigationgroups.py:21 +msgid "Footer" +msgstr "" + +#: module/page/extensions/navigationgroups.py:28 +#, fuzzy +#| msgid "in navigation" +msgid "navigation group" +msgstr "in navigazione" + +#: module/page/extensions/relatedpages.py:21 +msgid "Select pages that should be listed as related content." +msgstr "" + +#: module/page/extensions/relatedpages.py:26 +msgid "Related pages" +msgstr "" + +#: module/page/extensions/sites.py:21 +msgid "Site" +msgstr "" + +#: module/page/extensions/symlinks.py:22 +msgid "symlinked page" +msgstr "" + +#: module/page/extensions/symlinks.py:23 +msgid "All content is inherited from this page if given." +msgstr "Tutti i contenuti sono ereditate da questa pagina, se somministrata." + +#: module/page/extensions/titles.py:19 +msgid "content title" +msgstr "il titolo del contenuto" + +#: module/page/extensions/titles.py:22 +msgid "The first line is the main title, the following lines are subtitles." +msgstr "" +"La prima linea è il titolo principale, le seguenti righe sono i sottotitoli." + +#: module/page/extensions/titles.py:26 +msgid "page title" +msgstr "" + +#: module/page/extensions/titles.py:30 +#, fuzzy +#| msgid "Page title for browser window. Same as title by default." +msgid "" +"Page title for browser window. Same as title bydefault. Must not be longer " +"than 70 characters." +msgstr "" +"Titolo della pagina per la finestra del browser. Stesso titolo per " +"impostazione predefinita." + +#: module/page/extensions/titles.py:60 +msgid "Titles" +msgstr "" + +#: module/page/forms.py:187 msgid "This URL is already taken by an active page." msgstr "Questo URL è già stato preso da una pagina attiva." -#: module/page/forms.py:190 +#: module/page/forms.py:206 msgid "This URL is already taken by another active page." msgstr "Questo URL è già stato preso da un'altra pagina attiva." -#: module/page/modeladmins.py:41 +#: module/page/forms.py:211 +msgid "This page does not allow attachment of child pages" +msgstr "" + +#: module/page/modeladmins.py:42 msgid "Other options" msgstr "Altre opzioni" -#: module/page/modeladmins.py:90 module/page/models.py:174 +#: module/page/modeladmins.py:80 module/page/models.py:182 msgid "in navigation" msgstr "in navigazione" -#: module/page/modeladmins.py:105 +#: module/page/modeladmins.py:109 module/page/modeladmins.py:111 msgid "Add child page" msgstr "Aggiungi pagina figlio" -#: module/page/modeladmins.py:107 +#: module/page/modeladmins.py:120 module/page/modeladmins.py:122 #: templates/admin/feincms/content_inline.html:9 msgid "View on site" msgstr "Vedi sul sito" -#: module/page/modeladmins.py:126 +#: module/page/modeladmins.py:142 #, python-format -msgid "Add %(language)s Translation of \"%(page)s\"" +msgid "Add %(language)s translation of \"%(page)s\"" msgstr "" -#: module/page/modeladmins.py:156 +#: module/page/modeladmins.py:177 msgid "" "The content from the original translation has been copied to the newly " "created page." msgstr "" -#: module/page/modeladmins.py:170 +#: module/page/modeladmins.py:197 msgid "You don't have the necessary permissions to edit this object" msgstr "" -#: module/page/modeladmins.py:185 +#: module/page/modeladmins.py:223 msgid "inherited" msgstr "ereditato" -#: module/page/modeladmins.py:189 +#: module/page/modeladmins.py:229 msgid "extensions" msgstr "" -#: module/page/modeladmins.py:193 module/page/models.py:206 +#: module/page/modeladmins.py:233 module/page/models.py:221 msgid "is active" msgstr "" -#: module/page/models.py:167 +#: module/page/models.py:169 msgid "active" msgstr "attiva" -#: module/page/models.py:175 +#: module/page/models.py:173 +#, fuzzy +#| msgid "This is used for the generated navigation too." +msgid "This title is also used for navigation menu items." +msgstr "Questo viene utilizzato per la navigazione generato troppo." + +#: module/page/models.py:176 +msgid "This is used to build the URL for this page" +msgstr "" + +#: module/page/models.py:184 msgid "override URL" msgstr "override URL" -#: module/page/models.py:176 +#: module/page/models.py:186 msgid "" "Override the target URL. Be sure to include slashes at the beginning and at " -"the end if it is a local URL. This affects both the navigation and subpages'" -" URLs." -msgstr "Ignorare l'URL di destinazione. Assicurati di includere le barre all'inizio e alla fine se si tratta di un URL locale. Questo riguarda sia la navigazione e gli URL sottopagine '." +"the end if it is a local URL. This affects both the navigation and subpages' " +"URLs." +msgstr "" +"Ignorare l'URL di destinazione. Assicurati di includere le barre all'inizio " +"e alla fine se si tratta di un URL locale. Questo riguarda sia la " +"navigazione e gli URL sottopagine '." -#: module/page/models.py:177 +#: module/page/models.py:190 msgid "redirect to" msgstr "reindirizzamento" -#: module/page/models.py:178 +#: module/page/models.py:193 msgid "Target URL for automatic redirects or the primary key of a page." msgstr "" -#: module/page/models.py:180 +#: module/page/models.py:196 msgid "Cached URL" msgstr "Cache URL" -#: module/page/models.py:395 +#: module/page/models.py:298 +#, python-format +msgid "" +"This %(page_class)s uses a singleton template, and " +"FEINCMS_SINGLETON_TEMPLATE_DELETION_ALLOWED=False" +msgstr "" + +#: module/page/models.py:426 msgid "page" msgstr "pagina" -#: module/page/models.py:396 +#: module/page/models.py:427 msgid "pages" msgstr "pagine" -#: module/page/extensions/excerpt.py:9 -msgid "excerpt" -msgstr "" - -#: module/page/extensions/excerpt.py:10 -msgid "Add a brief excerpt summarizing the content of this page." -msgstr "" - -#: module/page/extensions/excerpt.py:12 -msgid "Excerpt" -msgstr "" - -#: module/page/extensions/navigation.py:82 -#: module/page/extensions/navigation.py:107 -msgid "navigation extension" -msgstr "" - -#: module/page/extensions/navigation.py:110 -msgid "" -"Select the module providing subpages for this page if you need to customize " -"the navigation." -msgstr "Selezionare il modulo che fornisce sottopagine per questa pagina, se avete bisogno di personalizzare la navigazione." - -#: module/page/extensions/navigation.py:125 -msgid "Navigation extension" -msgstr "" - -#: module/page/extensions/relatedpages.py:14 -msgid "Select pages that should be listed as related content." -msgstr "" - -#: module/page/extensions/relatedpages.py:19 -msgid "Related pages" -msgstr "" - -#: module/page/extensions/sites.py:16 -msgid "Site" -msgstr "" - -#: module/page/extensions/symlinks.py:15 -msgid "symlinked page" -msgstr "" - -#: module/page/extensions/symlinks.py:16 -msgid "All content is inherited from this page if given." -msgstr "Tutti i contenuti sono ereditate da questa pagina, se somministrata." - -#: module/page/extensions/titles.py:13 -msgid "content title" -msgstr "il titolo del contenuto" - -#: module/page/extensions/titles.py:14 -msgid "The first line is the main title, the following lines are subtitles." -msgstr "La prima linea è il titolo principale, le seguenti righe sono i sottotitoli." - -#: module/page/extensions/titles.py:15 -msgid "page title" -msgstr "" - -#: module/page/extensions/titles.py:16 -msgid "Page title for browser window. Same as title by default." -msgstr "Titolo della pagina per la finestra del browser. Stesso titolo per impostazione predefinita." - -#: module/page/extensions/titles.py:43 -msgid "Titles" -msgstr "" - -#: templates/admin/filter.html:3 -#, python-format -msgid " By %(filter_title)s " -msgstr "Da %(filter_title)s " - #: templates/admin/feincms/_messages_js.html:4 msgid "Really delete item?" msgstr "Cancellare questa voce?" @@ -756,7 +789,8 @@ msgstr "Impossibile eliminare voce" #: templates/admin/feincms/_messages_js.html:6 msgid "Cannot delete item, because it is parent of at least one other item." -msgstr "Impossibile eliminare voce, perché è madre di almeno un altro elemento." +msgstr "" +"Impossibile eliminare voce, perché è madre di almeno un altro elemento." #: templates/admin/feincms/_messages_js.html:7 msgid "Change template" @@ -769,9 +803,8 @@ msgstr "Davvero cambiare modello?
    Tutte le modifiche vengono salvate." #: templates/admin/feincms/_messages_js.html:9 #, python-format msgid "" -"Really change template?
    All changes are saved and content from " -"%%(source_regions)s is moved to " -"%%(target_region)s." +"Really change template?
    All changes are saved and content from " +"%%(source_regions)s is moved to %%(target_region)s." msgstr "" #: templates/admin/feincms/_messages_js.html:12 @@ -806,22 +839,24 @@ msgstr "Regione vuota" msgid "" "Content from the parent site is automatically inherited. To override this " "behaviour, add some content." -msgstr "Contenuti dal sito padre viene automaticamente ereditate. Per ignorare questo comportamento, aggiungere un po 'di contenuti." +msgstr "" +"Contenuti dal sito padre viene automaticamente ereditate. Per ignorare " +"questo comportamento, aggiungere un po 'di contenuti." #: templates/admin/feincms/content_editor.html:33 msgid "Add new item" msgstr "Aggiungi nuovo elemento" -#: templates/admin/feincms/content_inline.html:91 +#: templates/admin/feincms/content_inline.html:93 #, python-format msgid "Add another %(verbose_name)s" msgstr "" -#: templates/admin/feincms/content_inline.html:94 +#: templates/admin/feincms/content_inline.html:96 msgid "Remove" msgstr "" -#: templates/admin/feincms/fe_editor.html:40 +#: templates/admin/feincms/fe_editor.html:30 msgid "Save" msgstr "Salvare" @@ -849,13 +884,21 @@ msgstr "giù" msgid "remove" msgstr "rimuovere" -#: templates/admin/feincms/recover_form.html:8 -#: templates/admin/feincms/revision_form.html:8 +#: templates/admin/feincms/page/page/item_editor.html:10 +msgid "Edit on site" +msgstr "" + #: templates/admin/feincms/page/page/item_editor.html:23 #: templates/admin/feincms/page/page/tree_editor.html:7 +#: templates/admin/feincms/recover_form.html:8 +#: templates/admin/feincms/revision_form.html:8 msgid "Home" msgstr "Casa" +#: templates/admin/feincms/page/page/item_editor.html:27 +msgid "Add" +msgstr "" + #: templates/admin/feincms/recover_form.html:11 #, python-format msgid "Recover deleted %(verbose_name)s" @@ -878,29 +921,26 @@ msgstr "" msgid "Press the save button below to revert to this version of the object." msgstr "" -#: templates/admin/feincms/tree_editor.html:32 +#: templates/admin/feincms/tree_editor.html:22 msgid "Shortcuts" msgstr "Scorciatoie" -#: templates/admin/feincms/tree_editor.html:34 +#: templates/admin/feincms/tree_editor.html:24 msgid "Collapse tree" msgstr "" -#: templates/admin/feincms/tree_editor.html:35 +#: templates/admin/feincms/tree_editor.html:25 msgid "Expand tree" msgstr "" -#: templates/admin/feincms/tree_editor.html:38 +#: templates/admin/feincms/tree_editor.html:29 msgid "Filter" msgstr "Filtro" -#: templates/admin/feincms/page/page/item_editor.html:10 -msgid "Edit on site" -msgstr "" - -#: templates/admin/feincms/page/page/item_editor.html:27 -msgid "Add" -msgstr "" +#: templates/admin/filter.html:3 +#, python-format +msgid " By %(filter_title)s " +msgstr "Da %(filter_title)s " #: templates/admin/medialibrary/add_to_category.html:5 #: templates/admin/medialibrary/add_to_category.html:9 @@ -944,7 +984,8 @@ msgstr "" #, python-format msgid "" "\n" -" %(comment_username)s said on %(comment_submit_date)s
    \n" +" %(comment_username)s said on %(comment_submit_date)s
    \n" " " msgstr "" @@ -963,3 +1004,27 @@ msgstr "" #: templates/content/contactform/thanks.html:3 msgid "Thanks!" msgstr "Grazie!" + +#~ msgid "plain" +#~ msgstr "plain" + +#~ msgid "title row" +#~ msgstr "titolo di fila" + +#~ msgid "title row and column" +#~ msgstr "riga del titolo e colonna" + +#~ msgid "table" +#~ msgstr "tavolo" + +#~ msgid "tables" +#~ msgstr "tavoli" + +#~ msgid "data" +#~ msgstr "dati" + +#~ msgid "This will be prepended to the default keyword list." +#~ msgstr "Questo sarà anteposto al elenco di parole chiave predefinite." + +#~ msgid "This will be prepended to the default description." +#~ msgstr "Questo sarà anteposto alla descrizione di default." diff --git a/feincms/locale/nb/LC_MESSAGES/django.po b/feincms/locale/nb/LC_MESSAGES/django.po index 857e750a3..64115e488 100644 --- a/feincms/locale/nb/LC_MESSAGES/django.po +++ b/feincms/locale/nb/LC_MESSAGES/django.po @@ -1,744 +1,773 @@ # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. -# +# # Translators: # Håvard Grimelid , 2011 msgid "" msgstr "" "Project-Id-Version: FeinCMS\n" -"Report-Msgid-Bugs-To: http://github.com/feincms/feincms/issues\n" -"POT-Creation-Date: 2013-10-25 11:12+0200\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2014-07-20 14:59+0200\n" "PO-Revision-Date: 2013-10-25 09:15+0000\n" "Last-Translator: Matthias Kestenholz \n" -"Language-Team: Norwegian Bokmål (http://www.transifex.com/projects/p/feincms/language/nb/)\n" +"Language-Team: Norwegian Bokmål (http://www.transifex.com/projects/p/feincms/" +"language/nb/)\n" +"Language: nb\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Language: nb\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: models.py:370 content/template/models.py:59 -msgid "template" -msgstr "mal" - -#: models.py:510 -msgid "ordering" -msgstr "sortering" - -#: translations.py:259 module/blog/extensions/translations.py:17 -#: module/extensions/translations.py:123 -msgid "language" -msgstr "språk" - -#: admin/filterspecs.py:35 admin/filterspecs.py:70 +#: admin/filterspecs.py:47 admin/filterspecs.py:86 msgid "All" msgstr "Alle" -#: admin/filterspecs.py:46 module/page/models.py:172 +#: admin/filterspecs.py:58 module/page/models.py:178 msgid "Parent" msgstr "Forelder" -#: admin/filterspecs.py:81 +#: admin/filterspecs.py:97 #: templates/admin/medialibrary/mediafile/change_list.html:14 msgid "Category" msgstr "Kategori" -#: admin/item_editor.py:159 +#: admin/item_editor.py:190 #, python-format msgid "Change %s" msgstr "Endre %s" -#: admin/tree_editor.py:234 content/rss/models.py:20 -#: content/section/models.py:34 module/blog/models.py:29 -#: module/medialibrary/models.py:40 module/page/models.py:170 -#: module/page/models.py:226 +#: admin/tree_editor.py:294 content/rss/models.py:23 +#: content/section/models.py:35 module/blog/models.py:35 +#: module/medialibrary/models.py:45 module/page/models.py:172 +#: module/page/models.py:240 msgid "title" msgstr "tittel" -#: admin/tree_editor.py:407 +#: admin/tree_editor.py:353 admin/tree_editor.py:370 +msgid "You do not have permission to modify this object" +msgstr "" + +#: admin/tree_editor.py:491 +msgid "No permission" +msgstr "" + +#: admin/tree_editor.py:509 #, python-format msgid "%s has been moved to a new position." msgstr "%s har blitt flytta til en ny posisjon." -#: admin/tree_editor.py:411 +#: admin/tree_editor.py:512 msgid "Did not understand moving instruction." msgstr "Forstod ikke flytteinstruksjonen." -#: admin/tree_editor.py:420 +#: admin/tree_editor.py:523 msgid "actions" msgstr "aktiviteter" -#: admin/tree_editor.py:432 +#: admin/tree_editor.py:552 #, python-format -msgid "Successfully deleted %s items." +msgid "Successfully deleted %(count)d items." msgstr "" -#: admin/tree_editor.py:440 +#: admin/tree_editor.py:565 #, python-format msgid "Delete selected %(verbose_name_plural)s" msgstr "" -#: content/application/models.py:218 +#: content/application/models.py:147 msgid "application content" msgstr "applikasjonsinnhold" -#: content/application/models.py:219 +#: content/application/models.py:148 msgid "application contents" msgstr "applikasjonsinnhold" -#: content/application/models.py:249 +#: content/application/models.py:179 msgid "application" msgstr "applikasjon" -#: content/comments/models.py:24 +#: content/comments/models.py:32 msgid "enabled" msgstr "på" -#: content/comments/models.py:24 +#: content/comments/models.py:33 msgid "New comments may be added" msgstr "Nye kommentarer kan legges til" -#: content/comments/models.py:28 content/comments/models.py:29 +#: content/comments/models.py:37 content/comments/models.py:38 msgid "comments" msgstr "kommentarer" -#: content/comments/models.py:49 +#: content/comments/models.py:60 msgid "public" msgstr "offentlig" -#: content/comments/models.py:49 +#: content/comments/models.py:61 msgid "not public" msgstr "ikke offentlig" -#: content/contactform/models.py:18 +#: content/contactform/models.py:20 msgid "name" msgstr "navn" -#: content/contactform/models.py:19 +#: content/contactform/models.py:21 msgid "email" msgstr "epost" -#: content/contactform/models.py:20 +#: content/contactform/models.py:22 msgid "subject" msgstr "tema" -#: content/contactform/models.py:23 content/raw/models.py:14 +#: content/contactform/models.py:26 content/raw/models.py:16 msgid "content" msgstr "innhold" -#: content/contactform/models.py:34 +#: content/contactform/models.py:37 msgid "contact form" msgstr "kontaktskjema" -#: content/contactform/models.py:35 +#: content/contactform/models.py:38 msgid "contact forms" msgstr "kontaktskjemaer" -#: content/file/models.py:20 content/file/models.py:25 -#: module/medialibrary/models.py:84 +#: content/file/models.py:23 content/file/models.py:28 +#: module/medialibrary/models.py:94 msgid "file" msgstr "fil" -#: content/file/models.py:26 +#: content/file/models.py:29 msgid "files" msgstr "filer" -#: content/image/models.py:45 content/image/models.py:54 +#: content/image/models.py:46 content/image/models.py:55 msgid "image" msgstr "bilde" -#: content/image/models.py:48 +#: content/image/models.py:49 msgid "alternate text" msgstr "" -#: content/image/models.py:49 +#: content/image/models.py:50 msgid "Description of image" msgstr "" -#: content/image/models.py:50 module/medialibrary/models.py:231 +#: content/image/models.py:51 module/medialibrary/models.py:260 msgid "caption" msgstr "tittel" -#: content/image/models.py:55 +#: content/image/models.py:56 msgid "images" msgstr "bilder" -#: content/image/models.py:80 +#: content/image/models.py:82 msgid "position" msgstr "posisjon" -#: content/image/models.py:88 +#: content/image/models.py:90 msgid "format" msgstr "" -#: content/medialibrary/models.py:49 content/section/models.py:36 -#: module/medialibrary/fields.py:59 module/medialibrary/models.py:97 +#: content/medialibrary/models.py:51 content/section/models.py:38 +#: module/medialibrary/fields.py:66 module/medialibrary/models.py:112 msgid "media file" msgstr "mediafil" -#: content/medialibrary/models.py:50 module/medialibrary/models.py:98 +#: content/medialibrary/models.py:52 module/medialibrary/models.py:113 msgid "media files" msgstr "mediafiler" -#: content/medialibrary/models.py:58 content/section/models.py:52 -#: content/table/models.py:82 +#: content/medialibrary/models.py:64 content/section/models.py:59 msgid "type" msgstr "type" -#: content/raw/models.py:18 +#: content/raw/models.py:20 msgid "raw content" msgstr "råinnhold" -#: content/raw/models.py:19 +#: content/raw/models.py:21 msgid "raw contents" msgstr "råinnhold" -#: content/richtext/models.py:22 +#: content/richtext/models.py:24 msgid "HTML Tidy" msgstr "HTML Tidy" -#: content/richtext/models.py:23 +#: content/richtext/models.py:25 msgid "Ignore the HTML validation warnings" msgstr "Ignorer advarsler fra HTML-validering" -#: content/richtext/models.py:47 +#: content/richtext/models.py:55 #, python-format msgid "" "HTML validation produced %(count)d warnings. Please review the updated " "content below before continuing: %(messages)s" -msgstr "HTML-validering produserte %(count)d advarsler. Revider oppdatert innhold under før du fortsetter: %(messages)s" +msgstr "" +"HTML-validering produserte %(count)d advarsler. Revider oppdatert innhold " +"under før du fortsetter: %(messages)s" -#: content/richtext/models.py:84 content/section/models.py:35 +#: content/richtext/models.py:99 content/section/models.py:36 msgid "text" msgstr "tekst" -#: content/richtext/models.py:88 +#: content/richtext/models.py:103 msgid "rich text" msgstr "rik tekst" -#: content/richtext/models.py:89 +#: content/richtext/models.py:104 msgid "rich texts" msgstr "rike tekster" -#: content/rss/models.py:21 +#: content/rss/models.py:25 msgid "" "The rss field is updated several times a day. A change in the title will " "only be visible on the home page after the next feed update." -msgstr "RSS-strømmen blir oppdatert flere ganger om dagen. En endring i tittelel vil ikke bli synlig på nettsiden før etter neste oppdatering." +msgstr "" +"RSS-strømmen blir oppdatert flere ganger om dagen. En endring i tittelel vil " +"ikke bli synlig på nettsiden før etter neste oppdatering." -#: content/rss/models.py:22 +#: content/rss/models.py:28 msgid "link" msgstr "link" -#: content/rss/models.py:23 +#: content/rss/models.py:30 msgid "pre-rendered content" msgstr "forhåndsrendret innhold" -#: content/rss/models.py:24 +#: content/rss/models.py:32 msgid "last updated" msgstr "sist oppdatert" -#: content/rss/models.py:25 +#: content/rss/models.py:33 msgid "max. items" msgstr "maks antall objekter" -#: content/rss/models.py:29 +#: content/rss/models.py:37 msgid "RSS feed" msgstr "RSS-strøm" -#: content/rss/models.py:30 +#: content/rss/models.py:38 msgid "RSS feeds" msgstr "RSS-strømmer" -#: content/section/models.py:41 +#: content/section/models.py:43 msgid "section" msgstr "avsnitt" -#: content/section/models.py:42 +#: content/section/models.py:44 msgid "sections" msgstr "avsnitt" -#: content/table/models.py:63 -msgid "plain" -msgstr "enkel" - -#: content/table/models.py:64 -msgid "title row" -msgstr "tittelrad" - -#: content/table/models.py:66 -msgid "title row and column" -msgstr "tittelrad og kolonne" - -#: content/table/models.py:72 -msgid "table" -msgstr "tabell" - -#: content/table/models.py:73 -msgid "tables" -msgstr "tabeller" - -#: content/table/models.py:87 -msgid "data" -msgstr "data" - -#: content/template/models.py:51 +#: content/template/models.py:53 msgid "template content" msgstr "malinnhold" -#: content/template/models.py:52 +#: content/template/models.py:54 msgid "template contents" msgstr "malinnhold" -#: content/video/models.py:31 +#: content/template/models.py:63 models.py:400 +msgid "template" +msgstr "mal" + +#: content/video/models.py:34 msgid "video link" msgstr "videolink" -#: content/video/models.py:32 +#: content/video/models.py:36 msgid "" -"This should be a link to a youtube or vimeo video, i.e.: " -"http://www.youtube.com/watch?v=zmj1rpzDRZ0" -msgstr "Dette må være en link til en YouTube- eller Vimeo-film. F. eks. http://www.youtube.com/watch?v=zmj1rpzDRZ0" +"This should be a link to a youtube or vimeo video, i.e.: http://www.youtube." +"com/watch?v=zmj1rpzDRZ0" +msgstr "" +"Dette må være en link til en YouTube- eller Vimeo-film. F. eks. http://www." +"youtube.com/watch?v=zmj1rpzDRZ0" -#: content/video/models.py:37 +#: content/video/models.py:41 msgid "video" msgstr "video" -#: content/video/models.py:38 +#: content/video/models.py:42 msgid "videos" msgstr "videoer" -#: contrib/tagging.py:117 +#: contrib/tagging.py:132 msgid "Tagging" msgstr "" -#: module/blog/models.py:28 +#: models.py:550 +msgid "ordering" +msgstr "sortering" + +#: module/blog/extensions/tags.py:17 +msgid "tags" +msgstr "merkelapper" + +#: module/blog/extensions/translations.py:25 +#: module/extensions/translations.py:140 translations.py:282 +msgid "language" +msgstr "språk" + +#: module/blog/extensions/translations.py:35 +#: module/extensions/translations.py:148 +msgid "translation of" +msgstr "oversettelse av" + +#: module/blog/extensions/translations.py:39 +#: module/extensions/translations.py:152 +msgid "Leave this empty for entries in the primary language." +msgstr "La denne være tom for innlegg på primærspråket." + +#: module/blog/extensions/translations.py:67 +#: templates/admin/feincms/item_editor.html:33 +msgid "available translations" +msgstr "tilgjengelige oversettelser" + +#: module/blog/models.py:33 msgid "published" msgstr "publisert" -#: module/blog/models.py:30 +#: module/blog/models.py:36 msgid "This is used for the generated navigation too." msgstr "Denne vil også bli brukt for navigasjon." -#: module/blog/models.py:33 +#: module/blog/models.py:40 msgid "published on" msgstr "publisert på" -#: module/blog/models.py:34 -msgid "" -"Will be set automatically once you tick the `published` checkbox above." +#: module/blog/models.py:42 +msgid "Will be set automatically once you tick the `published` checkbox above." msgstr "Blir satt automatisk så snart `publisert`-boksen blir kryssa av." -#: module/blog/models.py:39 +#: module/blog/models.py:48 msgid "entry" msgstr "innlegg" -#: module/blog/models.py:40 +#: module/blog/models.py:49 msgid "entries" msgstr "innlegg" -#: module/blog/extensions/tags.py:12 -msgid "tags" -msgstr "merkelapper" - -#: module/blog/extensions/translations.py:20 -#: module/extensions/translations.py:126 -msgid "translation of" -msgstr "oversettelse av" - -#: module/blog/extensions/translations.py:23 -#: module/extensions/translations.py:129 -msgid "Leave this empty for entries in the primary language." -msgstr "La denne være tom for innlegg på primærspråket." - -#: module/blog/extensions/translations.py:44 -#: templates/admin/feincms/item_editor.html:43 -msgid "available translations" -msgstr "tilgjengelige oversettelser" - -#: module/extensions/changedate.py:32 +#: module/extensions/changedate.py:41 msgid "creation date" msgstr "opprettet dato" -#: module/extensions/changedate.py:33 +#: module/extensions/changedate.py:43 msgid "modification date" msgstr "endret dato" -#: module/extensions/ct_tracker.py:140 +#: module/extensions/ct_tracker.py:156 msgid "content types" msgstr "innholdstyper" -#: module/extensions/datepublisher.py:67 +#: module/extensions/datepublisher.py:86 msgid "publication date" msgstr "publiseringsdato" -#: module/extensions/datepublisher.py:70 +#: module/extensions/datepublisher.py:90 msgid "publication end date" msgstr "sluttdato for publisering" -#: module/extensions/datepublisher.py:72 +#: module/extensions/datepublisher.py:93 msgid "Leave empty if the entry should stay active forever." msgstr "La denne være tom dersom siden alltid skal være aktiv." -#: module/extensions/datepublisher.py:103 +#: module/extensions/datepublisher.py:128 msgid "visible from - to" msgstr "synlig fra - til" -#: module/extensions/datepublisher.py:113 +#: module/extensions/datepublisher.py:139 msgid "Date-based publishing" msgstr "Datobasert publisering" -#: module/extensions/featured.py:9 +#: module/extensions/featured.py:15 msgid "featured" msgstr "featured" -#: module/extensions/featured.py:14 +#: module/extensions/featured.py:21 msgid "Featured" msgstr "Featured" -#: module/extensions/seo.py:9 +#: module/extensions/seo.py:16 msgid "meta keywords" msgstr "meta-nøkkelord" -#: module/extensions/seo.py:10 -msgid "This will be prepended to the default keyword list." -msgstr "Dette vil bli lagt inn foran i standard nøkkelord-liste." +#: module/extensions/seo.py:18 +msgid "Keywords are ignored by most search engines." +msgstr "" -#: module/extensions/seo.py:11 +#: module/extensions/seo.py:20 msgid "meta description" msgstr "meta-beskrivelse" -#: module/extensions/seo.py:12 -msgid "This will be prepended to the default description." -msgstr "Dette vil bli lagt inn foran standard beskrivelse." +#: module/extensions/seo.py:22 +msgid "" +"This text is displayed on the search results page. It is however not used " +"for the SEO ranking. Text longer than 140 characters is truncated." +msgstr "" -#: module/extensions/seo.py:17 +#: module/extensions/seo.py:32 msgid "Search engine optimization" msgstr "Søkemotoroptimalisering" -#: module/extensions/translations.py:207 +#: module/extensions/translations.py:249 msgid "Edit translation" msgstr "Rediger oversettelse" -#: module/extensions/translations.py:210 +#: module/extensions/translations.py:256 msgid "Create translation" msgstr "Lag oversettelse" -#: module/extensions/translations.py:215 +#: module/extensions/translations.py:264 msgid "translations" msgstr "oversettelser" -#: module/medialibrary/forms.py:26 +#: module/medialibrary/forms.py:31 msgid "This would create a loop in the hierarchy" msgstr "" -#: module/medialibrary/forms.py:64 +#: module/medialibrary/forms.py:77 #, python-format msgid "" "Cannot overwrite with different file type (attempt to overwrite a " "%(old_ext)s with a %(new_ext)s)" msgstr "" -#: module/medialibrary/modeladmins.py:58 +#: module/medialibrary/modeladmins.py:63 #, python-format msgid "Successfully added %(count)d media file to %(category)s." msgid_plural "Successfully added %(count)d media files to %(category)s." msgstr[0] "" msgstr[1] "" -#: module/medialibrary/modeladmins.py:77 +#: module/medialibrary/modeladmins.py:84 msgid "Add selected media files to category" msgstr "" -#: module/medialibrary/modeladmins.py:86 +#: module/medialibrary/modeladmins.py:94 #, python-format msgid "ZIP file exported as %s" msgstr "" -#: module/medialibrary/modeladmins.py:88 +#: module/medialibrary/modeladmins.py:96 #, python-format msgid "ZIP file export failed: %s" msgstr "" -#: module/medialibrary/modeladmins.py:93 +#: module/medialibrary/modeladmins.py:104 msgid "Export selected media files as zip file" msgstr "" -#: module/medialibrary/modeladmins.py:136 +#: module/medialibrary/modeladmins.py:157 #: templates/admin/feincms/page/page/item_editor.html:15 msgid "Preview" msgstr "Forhåndsvisning" -#: module/medialibrary/modeladmins.py:141 module/medialibrary/models.py:88 +#: module/medialibrary/modeladmins.py:162 module/medialibrary/models.py:103 msgid "file size" msgstr "filstørrelse" -#: module/medialibrary/modeladmins.py:146 module/medialibrary/models.py:86 +#: module/medialibrary/modeladmins.py:167 module/medialibrary/models.py:100 msgid "created" msgstr "opprettet" -#: module/medialibrary/modeladmins.py:165 module/medialibrary/models.py:85 +#: module/medialibrary/modeladmins.py:187 module/medialibrary/models.py:97 msgid "file type" msgstr "filtype" -#: module/medialibrary/modeladmins.py:186 +#: module/medialibrary/modeladmins.py:211 msgid "file info" msgstr "filinfo" -#: module/medialibrary/modeladmins.py:198 +#: module/medialibrary/modeladmins.py:224 #, python-format msgid "%d files imported" msgstr "%d filer importert" -#: module/medialibrary/modeladmins.py:200 +#: module/medialibrary/modeladmins.py:226 #, python-format msgid "ZIP import failed: %s" msgstr "" -#: module/medialibrary/modeladmins.py:202 +#: module/medialibrary/modeladmins.py:228 msgid "No input file given" msgstr "Ingen inputfil gitt" -#: module/medialibrary/models.py:43 +#: module/medialibrary/models.py:49 msgid "parent" msgstr "forelder" -#: module/medialibrary/models.py:45 module/page/models.py:171 +#: module/medialibrary/models.py:51 module/page/models.py:175 msgid "slug" msgstr "slug" -#: module/medialibrary/models.py:49 +#: module/medialibrary/models.py:55 msgid "category" msgstr "kategori" -#: module/medialibrary/models.py:50 module/medialibrary/models.py:90 +#: module/medialibrary/models.py:56 module/medialibrary/models.py:106 msgid "categories" msgstr "kategorier" -#: module/medialibrary/models.py:87 +#: module/medialibrary/models.py:101 msgid "copyright" msgstr "copyright" -#: module/medialibrary/models.py:202 +#: module/medialibrary/models.py:219 msgid "Image" msgstr "Bilde" -#: module/medialibrary/models.py:203 +#: module/medialibrary/models.py:221 msgid "Video" msgstr "Video" -#: module/medialibrary/models.py:204 +#: module/medialibrary/models.py:224 msgid "Audio" msgstr "Lyd" -#: module/medialibrary/models.py:205 +#: module/medialibrary/models.py:226 msgid "PDF document" msgstr "PDF-dokument" -#: module/medialibrary/models.py:206 +#: module/medialibrary/models.py:227 msgid "Flash" msgstr "Flash" -#: module/medialibrary/models.py:207 +#: module/medialibrary/models.py:228 msgid "Text" msgstr "Tekst" -#: module/medialibrary/models.py:208 +#: module/medialibrary/models.py:229 msgid "Rich Text" msgstr "Rik tekst" -#: module/medialibrary/models.py:209 +#: module/medialibrary/models.py:230 msgid "Zip archive" msgstr "ZIP-arkiv" -#: module/medialibrary/models.py:210 +#: module/medialibrary/models.py:231 msgid "Microsoft Word" msgstr "Microsoft Word" -#: module/medialibrary/models.py:211 +#: module/medialibrary/models.py:233 msgid "Microsoft Excel" msgstr "Microsoft Excel" -#: module/medialibrary/models.py:212 +#: module/medialibrary/models.py:235 msgid "Microsoft PowerPoint" msgstr "Microsoft PowerPoint" -#: module/medialibrary/models.py:213 +#: module/medialibrary/models.py:237 msgid "Binary" msgstr "Binær" -#: module/medialibrary/models.py:232 +#: module/medialibrary/models.py:261 msgid "description" msgstr "beskrivelse" -#: module/medialibrary/models.py:235 +#: module/medialibrary/models.py:264 msgid "media file translation" msgstr "mediafil-oversettelse" -#: module/medialibrary/models.py:236 +#: module/medialibrary/models.py:265 msgid "media file translations" msgstr "mediafil-oversettelser" -#: module/page/forms.py:172 +#: module/page/extensions/excerpt.py:18 +msgid "excerpt" +msgstr "utdrag" + +#: module/page/extensions/excerpt.py:21 +msgid "Add a brief excerpt summarizing the content of this page." +msgstr "Legg til et kort sammendrag av innholdet på denne siden." + +#: module/page/extensions/excerpt.py:25 +msgid "Excerpt" +msgstr "Utdrag" + +#: module/page/extensions/navigation.py:86 +#: module/page/extensions/navigation.py:115 +msgid "navigation extension" +msgstr "navigasjonsutvidelse" + +#: module/page/extensions/navigation.py:119 +msgid "" +"Select the module providing subpages for this page if you need to customize " +"the navigation." +msgstr "" +"Velg modulen som skal bidra med undersider til denne siden dersom du trenger " +"å tilpasse navigasjonen." + +#: module/page/extensions/navigation.py:134 +msgid "Navigation extension" +msgstr "Navigasjonsutvidelse" + +#: module/page/extensions/navigationgroups.py:20 +msgid "Default" +msgstr "" + +#: module/page/extensions/navigationgroups.py:21 +msgid "Footer" +msgstr "" + +#: module/page/extensions/navigationgroups.py:28 +#, fuzzy +#| msgid "in navigation" +msgid "navigation group" +msgstr "i navigasjon" + +#: module/page/extensions/relatedpages.py:21 +msgid "Select pages that should be listed as related content." +msgstr "Velg sidene som skal listes som relatert innhold." + +#: module/page/extensions/relatedpages.py:26 +msgid "Related pages" +msgstr "Relaterte sider" + +#: module/page/extensions/sites.py:21 +msgid "Site" +msgstr "" + +#: module/page/extensions/symlinks.py:22 +msgid "symlinked page" +msgstr "symbolsk linket side" + +#: module/page/extensions/symlinks.py:23 +msgid "All content is inherited from this page if given." +msgstr "Dersom gitt, blir alt innhold blir arvet fra denne siden." + +#: module/page/extensions/titles.py:19 +msgid "content title" +msgstr "innholdstittel" + +#: module/page/extensions/titles.py:22 +msgid "The first line is the main title, the following lines are subtitles." +msgstr "Første linje er hovedtittelen, følgende linjer er undertitler." + +#: module/page/extensions/titles.py:26 +msgid "page title" +msgstr "sidetittel" + +#: module/page/extensions/titles.py:30 +#, fuzzy +#| msgid "Page title for browser window. Same as title by default." +msgid "" +"Page title for browser window. Same as title bydefault. Must not be longer " +"than 70 characters." +msgstr "Sidetittel for nettleservinduet. Samme som tittel som standard." + +#: module/page/extensions/titles.py:60 +msgid "Titles" +msgstr "Titler" + +#: module/page/forms.py:187 msgid "This URL is already taken by an active page." msgstr "Denne URL-en er allerede i bruk av en aktiv side." -#: module/page/forms.py:190 +#: module/page/forms.py:206 msgid "This URL is already taken by another active page." msgstr "Denne URL-en er allerede i bruk av annen en aktiv side." -#: module/page/modeladmins.py:41 +#: module/page/forms.py:211 +msgid "This page does not allow attachment of child pages" +msgstr "" + +#: module/page/modeladmins.py:42 msgid "Other options" msgstr "Andre valg" -#: module/page/modeladmins.py:90 module/page/models.py:174 +#: module/page/modeladmins.py:80 module/page/models.py:182 msgid "in navigation" msgstr "i navigasjon" -#: module/page/modeladmins.py:105 +#: module/page/modeladmins.py:109 module/page/modeladmins.py:111 msgid "Add child page" msgstr "Legg til underside" -#: module/page/modeladmins.py:107 +#: module/page/modeladmins.py:120 module/page/modeladmins.py:122 #: templates/admin/feincms/content_inline.html:9 msgid "View on site" msgstr "Vis på nettsted" -#: module/page/modeladmins.py:126 +#: module/page/modeladmins.py:142 #, python-format -msgid "Add %(language)s Translation of \"%(page)s\"" +msgid "Add %(language)s translation of \"%(page)s\"" msgstr "" -#: module/page/modeladmins.py:156 +#: module/page/modeladmins.py:177 msgid "" "The content from the original translation has been copied to the newly " "created page." msgstr "" -#: module/page/modeladmins.py:170 +#: module/page/modeladmins.py:197 msgid "You don't have the necessary permissions to edit this object" msgstr "" -#: module/page/modeladmins.py:185 +#: module/page/modeladmins.py:223 msgid "inherited" msgstr "arvet" -#: module/page/modeladmins.py:189 +#: module/page/modeladmins.py:229 msgid "extensions" msgstr "utvidelser" -#: module/page/modeladmins.py:193 module/page/models.py:206 +#: module/page/modeladmins.py:233 module/page/models.py:221 msgid "is active" msgstr "er aktiv" -#: module/page/models.py:167 +#: module/page/models.py:169 msgid "active" msgstr "aktiv" -#: module/page/models.py:175 +#: module/page/models.py:173 +#, fuzzy +#| msgid "This is used for the generated navigation too." +msgid "This title is also used for navigation menu items." +msgstr "Denne vil også bli brukt for navigasjon." + +#: module/page/models.py:176 +msgid "This is used to build the URL for this page" +msgstr "" + +#: module/page/models.py:184 msgid "override URL" msgstr "overstyr URL" -#: module/page/models.py:176 +#: module/page/models.py:186 msgid "" "Override the target URL. Be sure to include slashes at the beginning and at " -"the end if it is a local URL. This affects both the navigation and subpages'" -" URLs." -msgstr "Overstyr mål-URL. Inkluder skråstrek ved starten og ved slutten dersom det er en lokal URL. Dette gjelder både for navigasjons- og underside-URL-er." +"the end if it is a local URL. This affects both the navigation and subpages' " +"URLs." +msgstr "" +"Overstyr mål-URL. Inkluder skråstrek ved starten og ved slutten dersom det " +"er en lokal URL. Dette gjelder både for navigasjons- og underside-URL-er." -#: module/page/models.py:177 +#: module/page/models.py:190 msgid "redirect to" msgstr "videresend til" -#: module/page/models.py:178 +#: module/page/models.py:193 msgid "Target URL for automatic redirects or the primary key of a page." msgstr "" -#: module/page/models.py:180 +#: module/page/models.py:196 msgid "Cached URL" msgstr "Mellomlagret URL" -#: module/page/models.py:395 +#: module/page/models.py:298 +#, python-format +msgid "" +"This %(page_class)s uses a singleton template, and " +"FEINCMS_SINGLETON_TEMPLATE_DELETION_ALLOWED=False" +msgstr "" + +#: module/page/models.py:426 msgid "page" msgstr "side" -#: module/page/models.py:396 +#: module/page/models.py:427 msgid "pages" msgstr "sider" -#: module/page/extensions/excerpt.py:9 -msgid "excerpt" -msgstr "utdrag" - -#: module/page/extensions/excerpt.py:10 -msgid "Add a brief excerpt summarizing the content of this page." -msgstr "Legg til et kort sammendrag av innholdet på denne siden." - -#: module/page/extensions/excerpt.py:12 -msgid "Excerpt" -msgstr "Utdrag" - -#: module/page/extensions/navigation.py:82 -#: module/page/extensions/navigation.py:107 -msgid "navigation extension" -msgstr "navigasjonsutvidelse" - -#: module/page/extensions/navigation.py:110 -msgid "" -"Select the module providing subpages for this page if you need to customize " -"the navigation." -msgstr "Velg modulen som skal bidra med undersider til denne siden dersom du trenger å tilpasse navigasjonen." - -#: module/page/extensions/navigation.py:125 -msgid "Navigation extension" -msgstr "Navigasjonsutvidelse" - -#: module/page/extensions/relatedpages.py:14 -msgid "Select pages that should be listed as related content." -msgstr "Velg sidene som skal listes som relatert innhold." - -#: module/page/extensions/relatedpages.py:19 -msgid "Related pages" -msgstr "Relaterte sider" - -#: module/page/extensions/sites.py:16 -msgid "Site" -msgstr "" - -#: module/page/extensions/symlinks.py:15 -msgid "symlinked page" -msgstr "symbolsk linket side" - -#: module/page/extensions/symlinks.py:16 -msgid "All content is inherited from this page if given." -msgstr "Dersom gitt, blir alt innhold blir arvet fra denne siden." - -#: module/page/extensions/titles.py:13 -msgid "content title" -msgstr "innholdstittel" - -#: module/page/extensions/titles.py:14 -msgid "The first line is the main title, the following lines are subtitles." -msgstr "Første linje er hovedtittelen, følgende linjer er undertitler." - -#: module/page/extensions/titles.py:15 -msgid "page title" -msgstr "sidetittel" - -#: module/page/extensions/titles.py:16 -msgid "Page title for browser window. Same as title by default." -msgstr "Sidetittel for nettleservinduet. Samme som tittel som standard." - -#: module/page/extensions/titles.py:43 -msgid "Titles" -msgstr "Titler" - -#: templates/admin/filter.html:3 -#, python-format -msgid " By %(filter_title)s " -msgstr "For %(filter_title)s " - #: templates/admin/feincms/_messages_js.html:4 msgid "Really delete item?" msgstr "Slette objektet?" @@ -757,7 +786,8 @@ msgstr "Kan ikke slette objekt" #: templates/admin/feincms/_messages_js.html:6 msgid "Cannot delete item, because it is parent of at least one other item." -msgstr "Kan ikke slette objektet fordi det er foreldre til minst ett annet objekt." +msgstr "" +"Kan ikke slette objektet fordi det er foreldre til minst ett annet objekt." #: templates/admin/feincms/_messages_js.html:7 msgid "Change template" @@ -770,9 +800,8 @@ msgstr "Endre mal?
    Alle endringer blir lagret." #: templates/admin/feincms/_messages_js.html:9 #, python-format msgid "" -"Really change template?
    All changes are saved and content from " -"%%(source_regions)s is moved to " -"%%(target_region)s." +"Really change template?
    All changes are saved and content from " +"%%(source_regions)s is moved to %%(target_region)s." msgstr "" #: templates/admin/feincms/_messages_js.html:12 @@ -807,22 +836,24 @@ msgstr "Tom region" msgid "" "Content from the parent site is automatically inherited. To override this " "behaviour, add some content." -msgstr "Innhold fra forelder-side blir arvet automatisk. Overstyr dette ved å legge til innhold." +msgstr "" +"Innhold fra forelder-side blir arvet automatisk. Overstyr dette ved å legge " +"til innhold." #: templates/admin/feincms/content_editor.html:33 msgid "Add new item" msgstr "Legg til nytt objekt" -#: templates/admin/feincms/content_inline.html:91 +#: templates/admin/feincms/content_inline.html:93 #, python-format msgid "Add another %(verbose_name)s" msgstr "" -#: templates/admin/feincms/content_inline.html:94 +#: templates/admin/feincms/content_inline.html:96 msgid "Remove" msgstr "" -#: templates/admin/feincms/fe_editor.html:40 +#: templates/admin/feincms/fe_editor.html:30 msgid "Save" msgstr "Lagre" @@ -850,13 +881,21 @@ msgstr "ned" msgid "remove" msgstr "fjern" -#: templates/admin/feincms/recover_form.html:8 -#: templates/admin/feincms/revision_form.html:8 +#: templates/admin/feincms/page/page/item_editor.html:10 +msgid "Edit on site" +msgstr "Rediger på nettsted" + #: templates/admin/feincms/page/page/item_editor.html:23 #: templates/admin/feincms/page/page/tree_editor.html:7 +#: templates/admin/feincms/recover_form.html:8 +#: templates/admin/feincms/revision_form.html:8 msgid "Home" msgstr "Hjem" +#: templates/admin/feincms/page/page/item_editor.html:27 +msgid "Add" +msgstr "" + #: templates/admin/feincms/recover_form.html:11 #, python-format msgid "Recover deleted %(verbose_name)s" @@ -879,29 +918,26 @@ msgstr "" msgid "Press the save button below to revert to this version of the object." msgstr "" -#: templates/admin/feincms/tree_editor.html:32 +#: templates/admin/feincms/tree_editor.html:22 msgid "Shortcuts" msgstr "Snarveier" -#: templates/admin/feincms/tree_editor.html:34 +#: templates/admin/feincms/tree_editor.html:24 msgid "Collapse tree" msgstr "Slå sammen tre" -#: templates/admin/feincms/tree_editor.html:35 +#: templates/admin/feincms/tree_editor.html:25 msgid "Expand tree" msgstr "Utvid tre" -#: templates/admin/feincms/tree_editor.html:38 +#: templates/admin/feincms/tree_editor.html:29 msgid "Filter" msgstr "Filter" -#: templates/admin/feincms/page/page/item_editor.html:10 -msgid "Edit on site" -msgstr "Rediger på nettsted" - -#: templates/admin/feincms/page/page/item_editor.html:27 -msgid "Add" -msgstr "" +#: templates/admin/filter.html:3 +#, python-format +msgid " By %(filter_title)s " +msgstr "For %(filter_title)s " #: templates/admin/medialibrary/add_to_category.html:5 #: templates/admin/medialibrary/add_to_category.html:9 @@ -945,9 +981,14 @@ msgstr "%(comment_count)s kommentarer." #, python-format msgid "" "\n" -" %(comment_username)s said on %(comment_submit_date)s
    \n" +" %(comment_username)s said on %(comment_submit_date)s
    \n" +" " +msgstr "" +"\n" +" %(comment_username)s sa på %(comment_submit_date)s
    \n" " " -msgstr "\n %(comment_username)s sa på %(comment_submit_date)s
    \n " #: templates/content/comments/default.html:28 msgid "No comments." @@ -964,3 +1005,27 @@ msgstr "Send inn" #: templates/content/contactform/thanks.html:3 msgid "Thanks!" msgstr "Takk!" + +#~ msgid "plain" +#~ msgstr "enkel" + +#~ msgid "title row" +#~ msgstr "tittelrad" + +#~ msgid "title row and column" +#~ msgstr "tittelrad og kolonne" + +#~ msgid "table" +#~ msgstr "tabell" + +#~ msgid "tables" +#~ msgstr "tabeller" + +#~ msgid "data" +#~ msgstr "data" + +#~ msgid "This will be prepended to the default keyword list." +#~ msgstr "Dette vil bli lagt inn foran i standard nøkkelord-liste." + +#~ msgid "This will be prepended to the default description." +#~ msgstr "Dette vil bli lagt inn foran standard beskrivelse." diff --git a/feincms/locale/nl/LC_MESSAGES/django.po b/feincms/locale/nl/LC_MESSAGES/django.po index 56b8a811e..aa036021d 100644 --- a/feincms/locale/nl/LC_MESSAGES/django.po +++ b/feincms/locale/nl/LC_MESSAGES/django.po @@ -1,744 +1,785 @@ # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. -# +# # Translators: # bjornpost , 2013 msgid "" msgstr "" "Project-Id-Version: FeinCMS\n" -"Report-Msgid-Bugs-To: http://github.com/feincms/feincms/issues\n" -"POT-Creation-Date: 2013-10-25 11:12+0200\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2014-07-20 14:59+0200\n" "PO-Revision-Date: 2013-10-25 09:15+0000\n" "Last-Translator: Matthias Kestenholz \n" -"Language-Team: Dutch (http://www.transifex.com/projects/p/feincms/language/nl/)\n" +"Language-Team: Dutch (http://www.transifex.com/projects/p/feincms/language/" +"nl/)\n" +"Language: nl\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Language: nl\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: models.py:370 content/template/models.py:59 -msgid "template" -msgstr "template" - -#: models.py:510 -msgid "ordering" -msgstr "volgorde" - -#: translations.py:259 module/blog/extensions/translations.py:17 -#: module/extensions/translations.py:123 -msgid "language" -msgstr "taal" - -#: admin/filterspecs.py:35 admin/filterspecs.py:70 +#: admin/filterspecs.py:47 admin/filterspecs.py:86 msgid "All" msgstr "Alle" -#: admin/filterspecs.py:46 module/page/models.py:172 +#: admin/filterspecs.py:58 module/page/models.py:178 msgid "Parent" msgstr "Ouder" -#: admin/filterspecs.py:81 +#: admin/filterspecs.py:97 #: templates/admin/medialibrary/mediafile/change_list.html:14 msgid "Category" msgstr "Categorie" -#: admin/item_editor.py:159 +#: admin/item_editor.py:190 #, python-format msgid "Change %s" msgstr "%s veranderen" -#: admin/tree_editor.py:234 content/rss/models.py:20 -#: content/section/models.py:34 module/blog/models.py:29 -#: module/medialibrary/models.py:40 module/page/models.py:170 -#: module/page/models.py:226 +#: admin/tree_editor.py:294 content/rss/models.py:23 +#: content/section/models.py:35 module/blog/models.py:35 +#: module/medialibrary/models.py:45 module/page/models.py:172 +#: module/page/models.py:240 msgid "title" msgstr "titel" -#: admin/tree_editor.py:407 +#: admin/tree_editor.py:353 admin/tree_editor.py:370 +#, fuzzy +#| msgid "You don't have the necessary permissions to edit this object" +msgid "You do not have permission to modify this object" +msgstr "Je hebt niet de juiste rechten om dit object aan te passen" + +#: admin/tree_editor.py:491 +msgid "No permission" +msgstr "" + +#: admin/tree_editor.py:509 #, python-format msgid "%s has been moved to a new position." msgstr "%s is verplaatst." -#: admin/tree_editor.py:411 +#: admin/tree_editor.py:512 msgid "Did not understand moving instruction." msgstr "Begreep verplaats instructie niet" -#: admin/tree_editor.py:420 +#: admin/tree_editor.py:523 msgid "actions" msgstr "acties" -#: admin/tree_editor.py:432 -#, python-format -msgid "Successfully deleted %s items." +#: admin/tree_editor.py:552 +#, fuzzy, python-format +#| msgid "Successfully deleted %s items." +msgid "Successfully deleted %(count)d items." msgstr "%s items succesvol verwijderd." -#: admin/tree_editor.py:440 +#: admin/tree_editor.py:565 #, python-format msgid "Delete selected %(verbose_name_plural)s" msgstr "Geselecteerde %(verbose_name_plural)s verwijderen" -#: content/application/models.py:218 +#: content/application/models.py:147 msgid "application content" msgstr "applicatie content" -#: content/application/models.py:219 +#: content/application/models.py:148 msgid "application contents" msgstr "applicatie contents" -#: content/application/models.py:249 +#: content/application/models.py:179 msgid "application" msgstr "applicatie" -#: content/comments/models.py:24 +#: content/comments/models.py:32 msgid "enabled" msgstr "actief" -#: content/comments/models.py:24 +#: content/comments/models.py:33 msgid "New comments may be added" msgstr "Nieuwe reacties kunnen worden toegevoegd" -#: content/comments/models.py:28 content/comments/models.py:29 +#: content/comments/models.py:37 content/comments/models.py:38 msgid "comments" msgstr "reacties" -#: content/comments/models.py:49 +#: content/comments/models.py:60 msgid "public" msgstr "gedeeld" -#: content/comments/models.py:49 +#: content/comments/models.py:61 msgid "not public" msgstr "niet gedeeld" -#: content/contactform/models.py:18 +#: content/contactform/models.py:20 msgid "name" msgstr "naam" -#: content/contactform/models.py:19 +#: content/contactform/models.py:21 msgid "email" msgstr "e-Mail" -#: content/contactform/models.py:20 +#: content/contactform/models.py:22 msgid "subject" msgstr "onderwerp" -#: content/contactform/models.py:23 content/raw/models.py:14 +#: content/contactform/models.py:26 content/raw/models.py:16 msgid "content" msgstr "inhoud" -#: content/contactform/models.py:34 +#: content/contactform/models.py:37 msgid "contact form" msgstr "contactformulier" -#: content/contactform/models.py:35 +#: content/contactform/models.py:38 msgid "contact forms" msgstr "contactformulieren" -#: content/file/models.py:20 content/file/models.py:25 -#: module/medialibrary/models.py:84 +#: content/file/models.py:23 content/file/models.py:28 +#: module/medialibrary/models.py:94 msgid "file" msgstr "bestand" -#: content/file/models.py:26 +#: content/file/models.py:29 msgid "files" msgstr "bestanden" -#: content/image/models.py:45 content/image/models.py:54 +#: content/image/models.py:46 content/image/models.py:55 msgid "image" msgstr "afbeelding" -#: content/image/models.py:48 +#: content/image/models.py:49 msgid "alternate text" msgstr "alternatieve tekst" -#: content/image/models.py:49 +#: content/image/models.py:50 msgid "Description of image" msgstr "Omschrijving van de afbeelding" -#: content/image/models.py:50 module/medialibrary/models.py:231 +#: content/image/models.py:51 module/medialibrary/models.py:260 msgid "caption" msgstr "onderschrift" -#: content/image/models.py:55 +#: content/image/models.py:56 msgid "images" msgstr "afbeeldingen" -#: content/image/models.py:80 +#: content/image/models.py:82 msgid "position" msgstr "positie" -#: content/image/models.py:88 +#: content/image/models.py:90 msgid "format" msgstr "formaat" -#: content/medialibrary/models.py:49 content/section/models.py:36 -#: module/medialibrary/fields.py:59 module/medialibrary/models.py:97 +#: content/medialibrary/models.py:51 content/section/models.py:38 +#: module/medialibrary/fields.py:66 module/medialibrary/models.py:112 msgid "media file" msgstr "media-bestand" -#: content/medialibrary/models.py:50 module/medialibrary/models.py:98 +#: content/medialibrary/models.py:52 module/medialibrary/models.py:113 msgid "media files" msgstr "media-bestanden" -#: content/medialibrary/models.py:58 content/section/models.py:52 -#: content/table/models.py:82 +#: content/medialibrary/models.py:64 content/section/models.py:59 msgid "type" msgstr "type" -#: content/raw/models.py:18 +#: content/raw/models.py:20 msgid "raw content" msgstr "Ruwe Inhoud" -#: content/raw/models.py:19 +#: content/raw/models.py:21 msgid "raw contents" msgstr "ruwe inhoud" -#: content/richtext/models.py:22 +#: content/richtext/models.py:24 msgid "HTML Tidy" msgstr "HTML Tidy" -#: content/richtext/models.py:23 +#: content/richtext/models.py:25 msgid "Ignore the HTML validation warnings" msgstr "Negeer de HTML validatie waarschuwingen" -#: content/richtext/models.py:47 +#: content/richtext/models.py:55 #, python-format msgid "" "HTML validation produced %(count)d warnings. Please review the updated " "content below before continuing: %(messages)s" -msgstr "HTML validatie leverde %(count)d waarschuwingen op. Kijk alsjeblieft de bijgewerkte content na voordat je verder gaat: %(messages)s" +msgstr "" +"HTML validatie leverde %(count)d waarschuwingen op. Kijk alsjeblieft de " +"bijgewerkte content na voordat je verder gaat: %(messages)s" -#: content/richtext/models.py:84 content/section/models.py:35 +#: content/richtext/models.py:99 content/section/models.py:36 msgid "text" msgstr "tekst" -#: content/richtext/models.py:88 +#: content/richtext/models.py:103 msgid "rich text" msgstr "opgemaakte tekst" -#: content/richtext/models.py:89 +#: content/richtext/models.py:104 msgid "rich texts" msgstr "opgemaakte teksten" -#: content/rss/models.py:21 +#: content/rss/models.py:25 msgid "" "The rss field is updated several times a day. A change in the title will " "only be visible on the home page after the next feed update." -msgstr "Het RSS veld wordt meerdere malen per dag geactualiseerd. Een verandering in de titel zal pas op de pagina verschijnen na de volgende update." +msgstr "" +"Het RSS veld wordt meerdere malen per dag geactualiseerd. Een verandering in " +"de titel zal pas op de pagina verschijnen na de volgende update." -#: content/rss/models.py:22 +#: content/rss/models.py:28 msgid "link" msgstr "Link" -#: content/rss/models.py:23 +#: content/rss/models.py:30 msgid "pre-rendered content" msgstr "Vooraf-gerenderde Inhoud" -#: content/rss/models.py:24 +#: content/rss/models.py:32 msgid "last updated" msgstr "Laatste update" -#: content/rss/models.py:25 +#: content/rss/models.py:33 msgid "max. items" msgstr "Maximaal aantal" -#: content/rss/models.py:29 +#: content/rss/models.py:37 msgid "RSS feed" msgstr "RSS feed" -#: content/rss/models.py:30 +#: content/rss/models.py:38 msgid "RSS feeds" msgstr "RSS feeds" -#: content/section/models.py:41 +#: content/section/models.py:43 msgid "section" msgstr "sectie" -#: content/section/models.py:42 +#: content/section/models.py:44 msgid "sections" msgstr "secties" -#: content/table/models.py:63 -msgid "plain" -msgstr "plat" - -#: content/table/models.py:64 -msgid "title row" -msgstr "titel rij" - -#: content/table/models.py:66 -msgid "title row and column" -msgstr "title rij en kolom" - -#: content/table/models.py:72 -msgid "table" -msgstr "tabel" - -#: content/table/models.py:73 -msgid "tables" -msgstr "tabellen" - -#: content/table/models.py:87 -msgid "data" -msgstr "data" - -#: content/template/models.py:51 +#: content/template/models.py:53 msgid "template content" msgstr "template content" -#: content/template/models.py:52 +#: content/template/models.py:54 msgid "template contents" msgstr "template contents" -#: content/video/models.py:31 +#: content/template/models.py:63 models.py:400 +msgid "template" +msgstr "template" + +#: content/video/models.py:34 msgid "video link" msgstr "video link" -#: content/video/models.py:32 +#: content/video/models.py:36 msgid "" -"This should be a link to a youtube or vimeo video, i.e.: " -"http://www.youtube.com/watch?v=zmj1rpzDRZ0" -msgstr "Dit moet een link naar een youtube of vimeo video zijn. Bijvoorbeeld: http://www.youtube.com/watch?v=zmj1rpzDRZ0" +"This should be a link to a youtube or vimeo video, i.e.: http://www.youtube." +"com/watch?v=zmj1rpzDRZ0" +msgstr "" +"Dit moet een link naar een youtube of vimeo video zijn. Bijvoorbeeld: http://" +"www.youtube.com/watch?v=zmj1rpzDRZ0" -#: content/video/models.py:37 +#: content/video/models.py:41 msgid "video" msgstr "video" -#: content/video/models.py:38 +#: content/video/models.py:42 msgid "videos" msgstr "videos" -#: contrib/tagging.py:117 +#: contrib/tagging.py:132 msgid "Tagging" msgstr "Taggen" -#: module/blog/models.py:28 +#: models.py:550 +msgid "ordering" +msgstr "volgorde" + +#: module/blog/extensions/tags.py:17 +msgid "tags" +msgstr "tags" + +#: module/blog/extensions/translations.py:25 +#: module/extensions/translations.py:140 translations.py:282 +msgid "language" +msgstr "taal" + +#: module/blog/extensions/translations.py:35 +#: module/extensions/translations.py:148 +msgid "translation of" +msgstr "vertaling van" + +#: module/blog/extensions/translations.py:39 +#: module/extensions/translations.py:152 +msgid "Leave this empty for entries in the primary language." +msgstr "Laat dit veld leeg voor bijdragen in de hoofd-taal." + +#: module/blog/extensions/translations.py:67 +#: templates/admin/feincms/item_editor.html:33 +msgid "available translations" +msgstr "Beschikbare vertalingen" + +#: module/blog/models.py:33 msgid "published" msgstr "gepubliceerd" -#: module/blog/models.py:30 +#: module/blog/models.py:36 msgid "This is used for the generated navigation too." msgstr "Dit wordt ook gebruikt voor de gegenereerde navigatie." -#: module/blog/models.py:33 +#: module/blog/models.py:40 msgid "published on" msgstr "gepubliceerd op" -#: module/blog/models.py:34 -msgid "" -"Will be set automatically once you tick the `published` checkbox above." -msgstr "Wordt automatisch eenmalig ingesteld zodra `gepubliceerd` aangevinkt is." +#: module/blog/models.py:42 +msgid "Will be set automatically once you tick the `published` checkbox above." +msgstr "" +"Wordt automatisch eenmalig ingesteld zodra `gepubliceerd` aangevinkt is." -#: module/blog/models.py:39 +#: module/blog/models.py:48 msgid "entry" msgstr "item" -#: module/blog/models.py:40 +#: module/blog/models.py:49 msgid "entries" msgstr "items" -#: module/blog/extensions/tags.py:12 -msgid "tags" -msgstr "tags" - -#: module/blog/extensions/translations.py:20 -#: module/extensions/translations.py:126 -msgid "translation of" -msgstr "vertaling van" - -#: module/blog/extensions/translations.py:23 -#: module/extensions/translations.py:129 -msgid "Leave this empty for entries in the primary language." -msgstr "Laat dit veld leeg voor bijdragen in de hoofd-taal." - -#: module/blog/extensions/translations.py:44 -#: templates/admin/feincms/item_editor.html:43 -msgid "available translations" -msgstr "Beschikbare vertalingen" - -#: module/extensions/changedate.py:32 +#: module/extensions/changedate.py:41 msgid "creation date" msgstr "gemaakt op" -#: module/extensions/changedate.py:33 +#: module/extensions/changedate.py:43 msgid "modification date" msgstr "laatst gewijzigd op" -#: module/extensions/ct_tracker.py:140 +#: module/extensions/ct_tracker.py:156 msgid "content types" msgstr "content types" -#: module/extensions/datepublisher.py:67 +#: module/extensions/datepublisher.py:86 msgid "publication date" msgstr "gepubliceerd op" -#: module/extensions/datepublisher.py:70 +#: module/extensions/datepublisher.py:90 msgid "publication end date" msgstr "gepubliceerd tot" -#: module/extensions/datepublisher.py:72 +#: module/extensions/datepublisher.py:93 msgid "Leave empty if the entry should stay active forever." msgstr "Leeglaten als het item voor altijd actief moet blijven." -#: module/extensions/datepublisher.py:103 +#: module/extensions/datepublisher.py:128 msgid "visible from - to" msgstr "zichtbaar van - tot" -#: module/extensions/datepublisher.py:113 +#: module/extensions/datepublisher.py:139 msgid "Date-based publishing" msgstr "Datum-gebaseerde publicering" -#: module/extensions/featured.py:9 +#: module/extensions/featured.py:15 msgid "featured" msgstr "aanbevolen" -#: module/extensions/featured.py:14 +#: module/extensions/featured.py:21 msgid "Featured" msgstr "Aanbevolen" -#: module/extensions/seo.py:9 +#: module/extensions/seo.py:16 msgid "meta keywords" msgstr "meta trefwoorden" -#: module/extensions/seo.py:10 -msgid "This will be prepended to the default keyword list." -msgstr "Deze begrippen worden voor in de standaard trefwoordenlijst ingevoegd." +#: module/extensions/seo.py:18 +msgid "Keywords are ignored by most search engines." +msgstr "" -#: module/extensions/seo.py:11 +#: module/extensions/seo.py:20 msgid "meta description" msgstr "meta beschrijving" -#: module/extensions/seo.py:12 -msgid "This will be prepended to the default description." -msgstr "Deze beschrijving wordt voor in de standaard beschrijving ingevoegd." +#: module/extensions/seo.py:22 +msgid "" +"This text is displayed on the search results page. It is however not used " +"for the SEO ranking. Text longer than 140 characters is truncated." +msgstr "" -#: module/extensions/seo.py:17 +#: module/extensions/seo.py:32 msgid "Search engine optimization" msgstr "Zoekmachine optimalisatie" -#: module/extensions/translations.py:207 +#: module/extensions/translations.py:249 msgid "Edit translation" msgstr "Wijzig vertaling" -#: module/extensions/translations.py:210 +#: module/extensions/translations.py:256 msgid "Create translation" msgstr "Maak vertaling" -#: module/extensions/translations.py:215 +#: module/extensions/translations.py:264 msgid "translations" msgstr "vertalingen" -#: module/medialibrary/forms.py:26 +#: module/medialibrary/forms.py:31 msgid "This would create a loop in the hierarchy" msgstr "Dit zou een oneindige lus maken in de hiërarchie" -#: module/medialibrary/forms.py:64 +#: module/medialibrary/forms.py:77 #, python-format msgid "" "Cannot overwrite with different file type (attempt to overwrite a " "%(old_ext)s with a %(new_ext)s)" -msgstr "Kan niet overschrijven met ander bestandstype (poging tot overschrijven van %(old_ext)s met %(new_ext)s)" +msgstr "" +"Kan niet overschrijven met ander bestandstype (poging tot overschrijven van " +"%(old_ext)s met %(new_ext)s)" -#: module/medialibrary/modeladmins.py:58 +#: module/medialibrary/modeladmins.py:63 #, python-format msgid "Successfully added %(count)d media file to %(category)s." msgid_plural "Successfully added %(count)d media files to %(category)s." msgstr[0] "%(count)d media file succesvol toegevoegd aan %(category)s" msgstr[1] "%(count)d media files succesvol toegevoegd aan %(category)s" -#: module/medialibrary/modeladmins.py:77 +#: module/medialibrary/modeladmins.py:84 msgid "Add selected media files to category" msgstr "Voeg geselecteerde media files toe aan categorie" -#: module/medialibrary/modeladmins.py:86 +#: module/medialibrary/modeladmins.py:94 #, python-format msgid "ZIP file exported as %s" msgstr "ZIP-bestand geexporteerd als %s" -#: module/medialibrary/modeladmins.py:88 +#: module/medialibrary/modeladmins.py:96 #, python-format msgid "ZIP file export failed: %s" msgstr "ZIP-bestand export mislukt: %s" -#: module/medialibrary/modeladmins.py:93 +#: module/medialibrary/modeladmins.py:104 msgid "Export selected media files as zip file" msgstr "Exporteer geselecteerde mediabestanden als ZIP-bestand" -#: module/medialibrary/modeladmins.py:136 +#: module/medialibrary/modeladmins.py:157 #: templates/admin/feincms/page/page/item_editor.html:15 msgid "Preview" msgstr "Voorbeeld" -#: module/medialibrary/modeladmins.py:141 module/medialibrary/models.py:88 +#: module/medialibrary/modeladmins.py:162 module/medialibrary/models.py:103 msgid "file size" msgstr "bestandsgrootte" -#: module/medialibrary/modeladmins.py:146 module/medialibrary/models.py:86 +#: module/medialibrary/modeladmins.py:167 module/medialibrary/models.py:100 msgid "created" msgstr "gemaakt" -#: module/medialibrary/modeladmins.py:165 module/medialibrary/models.py:85 +#: module/medialibrary/modeladmins.py:187 module/medialibrary/models.py:97 msgid "file type" msgstr "bestandstype" -#: module/medialibrary/modeladmins.py:186 +#: module/medialibrary/modeladmins.py:211 msgid "file info" msgstr "bestandsinformatie" -#: module/medialibrary/modeladmins.py:198 +#: module/medialibrary/modeladmins.py:224 #, python-format msgid "%d files imported" msgstr "%d bestanden geïmporteerd" -#: module/medialibrary/modeladmins.py:200 +#: module/medialibrary/modeladmins.py:226 #, python-format msgid "ZIP import failed: %s" msgstr "Importeren van ZIP-bestand mislukt: %s" -#: module/medialibrary/modeladmins.py:202 +#: module/medialibrary/modeladmins.py:228 msgid "No input file given" msgstr "Geen inputbestand opgegeven" -#: module/medialibrary/models.py:43 +#: module/medialibrary/models.py:49 msgid "parent" msgstr "ouder" -#: module/medialibrary/models.py:45 module/page/models.py:171 +#: module/medialibrary/models.py:51 module/page/models.py:175 msgid "slug" msgstr "slug" -#: module/medialibrary/models.py:49 +#: module/medialibrary/models.py:55 msgid "category" msgstr "categorie" -#: module/medialibrary/models.py:50 module/medialibrary/models.py:90 +#: module/medialibrary/models.py:56 module/medialibrary/models.py:106 msgid "categories" msgstr "categorieën" -#: module/medialibrary/models.py:87 +#: module/medialibrary/models.py:101 msgid "copyright" msgstr "copyright" -#: module/medialibrary/models.py:202 +#: module/medialibrary/models.py:219 msgid "Image" msgstr "Afbeelding" -#: module/medialibrary/models.py:203 +#: module/medialibrary/models.py:221 msgid "Video" msgstr "Video" -#: module/medialibrary/models.py:204 +#: module/medialibrary/models.py:224 msgid "Audio" msgstr "Audio" -#: module/medialibrary/models.py:205 +#: module/medialibrary/models.py:226 msgid "PDF document" msgstr "PDF document" -#: module/medialibrary/models.py:206 +#: module/medialibrary/models.py:227 msgid "Flash" msgstr "Flash" -#: module/medialibrary/models.py:207 +#: module/medialibrary/models.py:228 msgid "Text" msgstr "Tekst" -#: module/medialibrary/models.py:208 +#: module/medialibrary/models.py:229 msgid "Rich Text" msgstr "Opgemaakte Tekst" -#: module/medialibrary/models.py:209 +#: module/medialibrary/models.py:230 msgid "Zip archive" msgstr "Zip-archief" -#: module/medialibrary/models.py:210 +#: module/medialibrary/models.py:231 msgid "Microsoft Word" msgstr "Microsoft Word" -#: module/medialibrary/models.py:211 +#: module/medialibrary/models.py:233 msgid "Microsoft Excel" msgstr "Microsoft Excel" -#: module/medialibrary/models.py:212 +#: module/medialibrary/models.py:235 msgid "Microsoft PowerPoint" msgstr "Microsoft PowerPoint" -#: module/medialibrary/models.py:213 +#: module/medialibrary/models.py:237 msgid "Binary" msgstr "Binaire data" -#: module/medialibrary/models.py:232 +#: module/medialibrary/models.py:261 msgid "description" msgstr "omschrijving" -#: module/medialibrary/models.py:235 +#: module/medialibrary/models.py:264 msgid "media file translation" msgstr "Mediabestand-vertaling" -#: module/medialibrary/models.py:236 +#: module/medialibrary/models.py:265 msgid "media file translations" msgstr "Mediabestand-vertalingen" -#: module/page/forms.py:172 +#: module/page/extensions/excerpt.py:18 +msgid "excerpt" +msgstr "samenvatting" + +#: module/page/extensions/excerpt.py:21 +msgid "Add a brief excerpt summarizing the content of this page." +msgstr "" +"Voeg een korte samenvatting toe die de inhoud van deze pagina beschrijft." + +#: module/page/extensions/excerpt.py:25 +msgid "Excerpt" +msgstr "Samenvatting" + +#: module/page/extensions/navigation.py:86 +#: module/page/extensions/navigation.py:115 +msgid "navigation extension" +msgstr "navigatie-uitbreiding" + +#: module/page/extensions/navigation.py:119 +msgid "" +"Select the module providing subpages for this page if you need to customize " +"the navigation." +msgstr "" +"Kies de module die subpagina's voor deze pagina definieert als je de " +"navigatie wilt uitbreiden." + +#: module/page/extensions/navigation.py:134 +msgid "Navigation extension" +msgstr "Navigatie-uitbreiding" + +#: module/page/extensions/navigationgroups.py:20 +msgid "Default" +msgstr "" + +#: module/page/extensions/navigationgroups.py:21 +msgid "Footer" +msgstr "" + +#: module/page/extensions/navigationgroups.py:28 +#, fuzzy +#| msgid "in navigation" +msgid "navigation group" +msgstr "in navigatie" + +#: module/page/extensions/relatedpages.py:21 +msgid "Select pages that should be listed as related content." +msgstr "Selecteer pagina's die beschouwd moet worden als gerelateerd aan deze." + +#: module/page/extensions/relatedpages.py:26 +msgid "Related pages" +msgstr "Gerelateerde pagina's" + +#: module/page/extensions/sites.py:21 +msgid "Site" +msgstr "Site" + +#: module/page/extensions/symlinks.py:22 +msgid "symlinked page" +msgstr "verbonden pagina" + +#: module/page/extensions/symlinks.py:23 +msgid "All content is inherited from this page if given." +msgstr "" +"Als dit ingesteld is, dan wordt de inhoud geleverd door de aangegeven pagina." + +#: module/page/extensions/titles.py:19 +msgid "content title" +msgstr "content titel" + +#: module/page/extensions/titles.py:22 +msgid "The first line is the main title, the following lines are subtitles." +msgstr "" +"De eerste regel is de hoofd-titel, de overige regels vormen de ondertitel." + +#: module/page/extensions/titles.py:26 +msgid "page title" +msgstr "paginatitel" + +#: module/page/extensions/titles.py:30 +#, fuzzy +#| msgid "Page title for browser window. Same as title by default." +msgid "" +"Page title for browser window. Same as title bydefault. Must not be longer " +"than 70 characters." +msgstr "paginatitel voor het browservenster. Standaard gelijk aan de titel." + +#: module/page/extensions/titles.py:60 +msgid "Titles" +msgstr "Titels" + +#: module/page/forms.py:187 msgid "This URL is already taken by an active page." msgstr "Deze URL is al in gebruik door een actieve pagina." -#: module/page/forms.py:190 +#: module/page/forms.py:206 msgid "This URL is already taken by another active page." msgstr "Deze URL is al in gebruik door een andere actieve pagina." -#: module/page/modeladmins.py:41 +#: module/page/forms.py:211 +msgid "This page does not allow attachment of child pages" +msgstr "" + +#: module/page/modeladmins.py:42 msgid "Other options" msgstr "Overige opties" -#: module/page/modeladmins.py:90 module/page/models.py:174 +#: module/page/modeladmins.py:80 module/page/models.py:182 msgid "in navigation" msgstr "in navigatie" -#: module/page/modeladmins.py:105 +#: module/page/modeladmins.py:109 module/page/modeladmins.py:111 msgid "Add child page" msgstr "Voeg subpagina toe" -#: module/page/modeladmins.py:107 +#: module/page/modeladmins.py:120 module/page/modeladmins.py:122 #: templates/admin/feincms/content_inline.html:9 msgid "View on site" msgstr "Op de website bekijken" -#: module/page/modeladmins.py:126 +#: module/page/modeladmins.py:142 #, python-format -msgid "Add %(language)s Translation of \"%(page)s\"" +msgid "Add %(language)s translation of \"%(page)s\"" msgstr "" -#: module/page/modeladmins.py:156 +#: module/page/modeladmins.py:177 msgid "" "The content from the original translation has been copied to the newly " "created page." -msgstr "De inhoud van de originele vertaling is gekopieerd naar de aangemaakte pagina." +msgstr "" +"De inhoud van de originele vertaling is gekopieerd naar de aangemaakte " +"pagina." -#: module/page/modeladmins.py:170 +#: module/page/modeladmins.py:197 msgid "You don't have the necessary permissions to edit this object" msgstr "Je hebt niet de juiste rechten om dit object aan te passen" -#: module/page/modeladmins.py:185 +#: module/page/modeladmins.py:223 msgid "inherited" msgstr "geërfd" -#: module/page/modeladmins.py:189 +#: module/page/modeladmins.py:229 msgid "extensions" msgstr "uitbreidingen" -#: module/page/modeladmins.py:193 module/page/models.py:206 +#: module/page/modeladmins.py:233 module/page/models.py:221 msgid "is active" msgstr "is actief" -#: module/page/models.py:167 +#: module/page/models.py:169 msgid "active" msgstr "actief" -#: module/page/models.py:175 +#: module/page/models.py:173 +#, fuzzy +#| msgid "This is used for the generated navigation too." +msgid "This title is also used for navigation menu items." +msgstr "Dit wordt ook gebruikt voor de gegenereerde navigatie." + +#: module/page/models.py:176 +msgid "This is used to build the URL for this page" +msgstr "" + +#: module/page/models.py:184 msgid "override URL" msgstr "overschrijf URL" -#: module/page/models.py:176 +#: module/page/models.py:186 msgid "" "Override the target URL. Be sure to include slashes at the beginning and at " -"the end if it is a local URL. This affects both the navigation and subpages'" -" URLs." -msgstr "Overschrijft de URL. Als het een lokale URL betreft moet er aan aan weerszijden een / staan. Dit veld heeft betrekking op zowel de navigatie als de URLs van subpagina's." +"the end if it is a local URL. This affects both the navigation and subpages' " +"URLs." +msgstr "" +"Overschrijft de URL. Als het een lokale URL betreft moet er aan aan " +"weerszijden een / staan. Dit veld heeft betrekking op zowel de navigatie " +"als de URLs van subpagina's." -#: module/page/models.py:177 +#: module/page/models.py:190 msgid "redirect to" msgstr "doorsturen naar" -#: module/page/models.py:178 +#: module/page/models.py:193 msgid "Target URL for automatic redirects or the primary key of a page." msgstr "" -#: module/page/models.py:180 +#: module/page/models.py:196 msgid "Cached URL" msgstr "URL uit cache" -#: module/page/models.py:395 +#: module/page/models.py:298 +#, python-format +msgid "" +"This %(page_class)s uses a singleton template, and " +"FEINCMS_SINGLETON_TEMPLATE_DELETION_ALLOWED=False" +msgstr "" + +#: module/page/models.py:426 msgid "page" msgstr "pagina" -#: module/page/models.py:396 +#: module/page/models.py:427 msgid "pages" msgstr "pagina's" -#: module/page/extensions/excerpt.py:9 -msgid "excerpt" -msgstr "samenvatting" - -#: module/page/extensions/excerpt.py:10 -msgid "Add a brief excerpt summarizing the content of this page." -msgstr "Voeg een korte samenvatting toe die de inhoud van deze pagina beschrijft." - -#: module/page/extensions/excerpt.py:12 -msgid "Excerpt" -msgstr "Samenvatting" - -#: module/page/extensions/navigation.py:82 -#: module/page/extensions/navigation.py:107 -msgid "navigation extension" -msgstr "navigatie-uitbreiding" - -#: module/page/extensions/navigation.py:110 -msgid "" -"Select the module providing subpages for this page if you need to customize " -"the navigation." -msgstr "Kies de module die subpagina's voor deze pagina definieert als je de navigatie wilt uitbreiden." - -#: module/page/extensions/navigation.py:125 -msgid "Navigation extension" -msgstr "Navigatie-uitbreiding" - -#: module/page/extensions/relatedpages.py:14 -msgid "Select pages that should be listed as related content." -msgstr "Selecteer pagina's die beschouwd moet worden als gerelateerd aan deze." - -#: module/page/extensions/relatedpages.py:19 -msgid "Related pages" -msgstr "Gerelateerde pagina's" - -#: module/page/extensions/sites.py:16 -msgid "Site" -msgstr "Site" - -#: module/page/extensions/symlinks.py:15 -msgid "symlinked page" -msgstr "verbonden pagina" - -#: module/page/extensions/symlinks.py:16 -msgid "All content is inherited from this page if given." -msgstr "Als dit ingesteld is, dan wordt de inhoud geleverd door de aangegeven pagina." - -#: module/page/extensions/titles.py:13 -msgid "content title" -msgstr "content titel" - -#: module/page/extensions/titles.py:14 -msgid "The first line is the main title, the following lines are subtitles." -msgstr "De eerste regel is de hoofd-titel, de overige regels vormen de ondertitel." - -#: module/page/extensions/titles.py:15 -msgid "page title" -msgstr "paginatitel" - -#: module/page/extensions/titles.py:16 -msgid "Page title for browser window. Same as title by default." -msgstr "paginatitel voor het browservenster. Standaard gelijk aan de titel." - -#: module/page/extensions/titles.py:43 -msgid "Titles" -msgstr "Titels" - -#: templates/admin/filter.html:3 -#, python-format -msgid " By %(filter_title)s " -msgstr " Op %(filter_title)s" - #: templates/admin/feincms/_messages_js.html:4 msgid "Really delete item?" msgstr "Item echt verwijderen?" @@ -757,7 +798,9 @@ msgstr "Kan item niet verwijderen" #: templates/admin/feincms/_messages_js.html:6 msgid "Cannot delete item, because it is parent of at least one other item." -msgstr "Kan item niet verwijderen, omdat het de ouder is van een of meerdere sub-items." +msgstr "" +"Kan item niet verwijderen, omdat het de ouder is van een of meerdere sub-" +"items." #: templates/admin/feincms/_messages_js.html:7 msgid "Change template" @@ -770,10 +813,12 @@ msgstr "Template echt veranderen?
    Alle wijzigingen zijn opgeslagen." #: templates/admin/feincms/_messages_js.html:9 #, python-format msgid "" -"Really change template?
    All changes are saved and content from " -"%%(source_regions)s is moved to " -"%%(target_region)s." -msgstr "Template echt veranderen?
    Alle wijzigingen zijn opgeslagen en de inhoud van %%(source_regions)sis verplaatst naar %%(target_region)s." +"Really change template?
    All changes are saved and content from " +"%%(source_regions)s is moved to %%(target_region)s." +msgstr "" +"Template echt veranderen?
    Alle wijzigingen zijn opgeslagen en de " +"inhoud van %%(source_regions)sis verplaatst naar " +"%%(target_region)s." #: templates/admin/feincms/_messages_js.html:12 msgid "Hide" @@ -807,22 +852,24 @@ msgstr "Leeg gebied" msgid "" "Content from the parent site is automatically inherited. To override this " "behaviour, add some content." -msgstr "Inhoud van de ouder-pagina wordt automatisch geërfd. Voeg zelf inhoud toe om dit te veranderen." +msgstr "" +"Inhoud van de ouder-pagina wordt automatisch geërfd. Voeg zelf inhoud toe om " +"dit te veranderen." #: templates/admin/feincms/content_editor.html:33 msgid "Add new item" msgstr "Nieuw item toevoegen" -#: templates/admin/feincms/content_inline.html:91 +#: templates/admin/feincms/content_inline.html:93 #, python-format msgid "Add another %(verbose_name)s" msgstr "Voeg nog een %(verbose_name)s toe" -#: templates/admin/feincms/content_inline.html:94 +#: templates/admin/feincms/content_inline.html:96 msgid "Remove" msgstr "Verwijder" -#: templates/admin/feincms/fe_editor.html:40 +#: templates/admin/feincms/fe_editor.html:30 msgid "Save" msgstr "Opslaan" @@ -850,13 +897,21 @@ msgstr "naar onder" msgid "remove" msgstr "verwijderen" -#: templates/admin/feincms/recover_form.html:8 -#: templates/admin/feincms/revision_form.html:8 +#: templates/admin/feincms/page/page/item_editor.html:10 +msgid "Edit on site" +msgstr "Op de website wijzigen" + #: templates/admin/feincms/page/page/item_editor.html:23 #: templates/admin/feincms/page/page/tree_editor.html:7 +#: templates/admin/feincms/recover_form.html:8 +#: templates/admin/feincms/revision_form.html:8 msgid "Home" msgstr "Startpagina" +#: templates/admin/feincms/page/page/item_editor.html:27 +msgid "Add" +msgstr "Voeg toe" + #: templates/admin/feincms/recover_form.html:11 #, python-format msgid "Recover deleted %(verbose_name)s" @@ -879,29 +934,26 @@ msgstr "Herstel %(verbose_name)s" msgid "Press the save button below to revert to this version of the object." msgstr "Druk op de save-knop om deze versie van het object te herstellen." -#: templates/admin/feincms/tree_editor.html:32 +#: templates/admin/feincms/tree_editor.html:22 msgid "Shortcuts" msgstr "Snelkoppelingen" -#: templates/admin/feincms/tree_editor.html:34 +#: templates/admin/feincms/tree_editor.html:24 msgid "Collapse tree" msgstr "Alles dichtklappen" -#: templates/admin/feincms/tree_editor.html:35 +#: templates/admin/feincms/tree_editor.html:25 msgid "Expand tree" msgstr "Alles openklappen" -#: templates/admin/feincms/tree_editor.html:38 +#: templates/admin/feincms/tree_editor.html:29 msgid "Filter" msgstr "Filter" -#: templates/admin/feincms/page/page/item_editor.html:10 -msgid "Edit on site" -msgstr "Op de website wijzigen" - -#: templates/admin/feincms/page/page/item_editor.html:27 -msgid "Add" -msgstr "Voeg toe" +#: templates/admin/filter.html:3 +#, python-format +msgid " By %(filter_title)s " +msgstr " Op %(filter_title)s" #: templates/admin/medialibrary/add_to_category.html:5 #: templates/admin/medialibrary/add_to_category.html:9 @@ -914,7 +966,8 @@ msgstr "Selecteer categorie om toe te passen:" #: templates/admin/medialibrary/add_to_category.html:17 msgid "The following media files will be added to the selected category:" -msgstr "De volgende mediabestanden worden toegevoegd aan de geselecteerde categorie:" +msgstr "" +"De volgende mediabestanden worden toegevoegd aan de geselecteerde categorie:" #: templates/admin/medialibrary/add_to_category.html:22 msgid "Add to category" @@ -945,9 +998,14 @@ msgstr "%(comment_count)s reacties" #, python-format msgid "" "\n" -" %(comment_username)s said on %(comment_submit_date)s
    \n" +" %(comment_username)s said on %(comment_submit_date)s
    \n" +" " +msgstr "" +"\n" +" %(comment_username)s zei op %(comment_submit_date)s
    \n" " " -msgstr "\n %(comment_username)s zei op %(comment_submit_date)s
    \n " #: templates/content/comments/default.html:28 msgid "No comments." @@ -964,3 +1022,29 @@ msgstr "Verzenden" #: templates/content/contactform/thanks.html:3 msgid "Thanks!" msgstr "Bedankt!" + +#~ msgid "plain" +#~ msgstr "plat" + +#~ msgid "title row" +#~ msgstr "titel rij" + +#~ msgid "title row and column" +#~ msgstr "title rij en kolom" + +#~ msgid "table" +#~ msgstr "tabel" + +#~ msgid "tables" +#~ msgstr "tabellen" + +#~ msgid "data" +#~ msgstr "data" + +#~ msgid "This will be prepended to the default keyword list." +#~ msgstr "" +#~ "Deze begrippen worden voor in de standaard trefwoordenlijst ingevoegd." + +#~ msgid "This will be prepended to the default description." +#~ msgstr "" +#~ "Deze beschrijving wordt voor in de standaard beschrijving ingevoegd." diff --git a/feincms/locale/pl/LC_MESSAGES/django.po b/feincms/locale/pl/LC_MESSAGES/django.po index 4645e1b89..fff700744 100644 --- a/feincms/locale/pl/LC_MESSAGES/django.po +++ b/feincms/locale/pl/LC_MESSAGES/django.po @@ -1,446 +1,437 @@ # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. -# +# # Translators: # areksz , 2013 msgid "" msgstr "" "Project-Id-Version: FeinCMS\n" -"Report-Msgid-Bugs-To: http://github.com/feincms/feincms/issues\n" -"POT-Creation-Date: 2013-10-25 11:12+0200\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2014-07-20 14:59+0200\n" "PO-Revision-Date: 2013-10-25 09:15+0000\n" "Last-Translator: Matthias Kestenholz \n" -"Language-Team: Polish (http://www.transifex.com/projects/p/feincms/language/pl/)\n" +"Language-Team: Polish (http://www.transifex.com/projects/p/feincms/language/" +"pl/)\n" +"Language: pl\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Language: pl\n" -"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" - -#: models.py:370 content/template/models.py:59 -msgid "template" -msgstr "szablon" - -#: models.py:510 -msgid "ordering" -msgstr "sortowanie" +"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 " +"|| n%100>=20) ? 1 : 2);\n" -#: translations.py:259 module/blog/extensions/translations.py:17 -#: module/extensions/translations.py:123 -msgid "language" -msgstr "język" - -#: admin/filterspecs.py:35 admin/filterspecs.py:70 +#: admin/filterspecs.py:47 admin/filterspecs.py:86 msgid "All" msgstr "wszystko" -#: admin/filterspecs.py:46 module/page/models.py:172 +#: admin/filterspecs.py:58 module/page/models.py:178 msgid "Parent" msgstr "Rodzic" -#: admin/filterspecs.py:81 +#: admin/filterspecs.py:97 #: templates/admin/medialibrary/mediafile/change_list.html:14 msgid "Category" msgstr "Kategoria" -#: admin/item_editor.py:159 +#: admin/item_editor.py:190 #, python-format msgid "Change %s" msgstr "Zmień %s" -#: admin/tree_editor.py:234 content/rss/models.py:20 -#: content/section/models.py:34 module/blog/models.py:29 -#: module/medialibrary/models.py:40 module/page/models.py:170 -#: module/page/models.py:226 +#: admin/tree_editor.py:294 content/rss/models.py:23 +#: content/section/models.py:35 module/blog/models.py:35 +#: module/medialibrary/models.py:45 module/page/models.py:172 +#: module/page/models.py:240 msgid "title" msgstr "tytuł" -#: admin/tree_editor.py:407 +#: admin/tree_editor.py:353 admin/tree_editor.py:370 +#, fuzzy +#| msgid "You don't have the necessary permissions to edit this object" +msgid "You do not have permission to modify this object" +msgstr "Nie masz wystarczających uprawnień aby edytować ten element" + +#: admin/tree_editor.py:491 +msgid "No permission" +msgstr "" + +#: admin/tree_editor.py:509 #, python-format msgid "%s has been moved to a new position." msgstr "%s został przesunięty" -#: admin/tree_editor.py:411 +#: admin/tree_editor.py:512 msgid "Did not understand moving instruction." msgstr "Instrukcja przesunięcia nie jest zrozumiała." -#: admin/tree_editor.py:420 +#: admin/tree_editor.py:523 msgid "actions" msgstr "akcje" -#: admin/tree_editor.py:432 +#: admin/tree_editor.py:552 #, python-format -msgid "Successfully deleted %s items." +msgid "Successfully deleted %(count)d items." msgstr "" -#: admin/tree_editor.py:440 +#: admin/tree_editor.py:565 #, python-format msgid "Delete selected %(verbose_name_plural)s" msgstr "" -#: content/application/models.py:218 +#: content/application/models.py:147 msgid "application content" msgstr "zawartość aplikacji" -#: content/application/models.py:219 +#: content/application/models.py:148 msgid "application contents" msgstr "application content" -#: content/application/models.py:249 +#: content/application/models.py:179 msgid "application" msgstr "aplikacja" -#: content/comments/models.py:24 +#: content/comments/models.py:32 msgid "enabled" msgstr "aktywny" -#: content/comments/models.py:24 +#: content/comments/models.py:33 msgid "New comments may be added" msgstr "Nowe komentarze mogą być dodane" -#: content/comments/models.py:28 content/comments/models.py:29 +#: content/comments/models.py:37 content/comments/models.py:38 msgid "comments" msgstr "komentarze" -#: content/comments/models.py:49 +#: content/comments/models.py:60 msgid "public" msgstr "publiczne" -#: content/comments/models.py:49 +#: content/comments/models.py:61 msgid "not public" msgstr "nie publiczne" -#: content/contactform/models.py:18 +#: content/contactform/models.py:20 msgid "name" msgstr "nazwa" -#: content/contactform/models.py:19 +#: content/contactform/models.py:21 msgid "email" msgstr "email" -#: content/contactform/models.py:20 +#: content/contactform/models.py:22 msgid "subject" msgstr "temat" -#: content/contactform/models.py:23 content/raw/models.py:14 +#: content/contactform/models.py:26 content/raw/models.py:16 msgid "content" msgstr "zawartość" -#: content/contactform/models.py:34 +#: content/contactform/models.py:37 msgid "contact form" msgstr "formularz kontaktowy" -#: content/contactform/models.py:35 +#: content/contactform/models.py:38 msgid "contact forms" msgstr "formularze kontaktowe" -#: content/file/models.py:20 content/file/models.py:25 -#: module/medialibrary/models.py:84 +#: content/file/models.py:23 content/file/models.py:28 +#: module/medialibrary/models.py:94 msgid "file" msgstr "plik" -#: content/file/models.py:26 +#: content/file/models.py:29 msgid "files" msgstr "pliki" -#: content/image/models.py:45 content/image/models.py:54 +#: content/image/models.py:46 content/image/models.py:55 msgid "image" msgstr "zdjęcie" -#: content/image/models.py:48 +#: content/image/models.py:49 msgid "alternate text" msgstr "" -#: content/image/models.py:49 +#: content/image/models.py:50 msgid "Description of image" msgstr "" -#: content/image/models.py:50 module/medialibrary/models.py:231 +#: content/image/models.py:51 module/medialibrary/models.py:260 msgid "caption" msgstr "" -#: content/image/models.py:55 +#: content/image/models.py:56 msgid "images" msgstr "zdjęcia" -#: content/image/models.py:80 +#: content/image/models.py:82 msgid "position" msgstr "pozycja" -#: content/image/models.py:88 +#: content/image/models.py:90 msgid "format" msgstr "" -#: content/medialibrary/models.py:49 content/section/models.py:36 -#: module/medialibrary/fields.py:59 module/medialibrary/models.py:97 +#: content/medialibrary/models.py:51 content/section/models.py:38 +#: module/medialibrary/fields.py:66 module/medialibrary/models.py:112 msgid "media file" msgstr "plik media" -#: content/medialibrary/models.py:50 module/medialibrary/models.py:98 +#: content/medialibrary/models.py:52 module/medialibrary/models.py:113 msgid "media files" msgstr "pliki media" -#: content/medialibrary/models.py:58 content/section/models.py:52 -#: content/table/models.py:82 +#: content/medialibrary/models.py:64 content/section/models.py:59 msgid "type" msgstr "typ" -#: content/raw/models.py:18 +#: content/raw/models.py:20 msgid "raw content" msgstr "tekst niesformatowany" -#: content/raw/models.py:19 +#: content/raw/models.py:21 msgid "raw contents" msgstr "teksty niesformatowane" -#: content/richtext/models.py:22 +#: content/richtext/models.py:24 msgid "HTML Tidy" msgstr "HTML Tidy" -#: content/richtext/models.py:23 +#: content/richtext/models.py:25 msgid "Ignore the HTML validation warnings" msgstr "Ignoruj ostrzeżenia o walidacji HMTL" -#: content/richtext/models.py:47 +#: content/richtext/models.py:55 #, python-format msgid "" "HTML validation produced %(count)d warnings. Please review the updated " "content below before continuing: %(messages)s" msgstr "" -#: content/richtext/models.py:84 content/section/models.py:35 +#: content/richtext/models.py:99 content/section/models.py:36 msgid "text" msgstr "tekst" -#: content/richtext/models.py:88 +#: content/richtext/models.py:103 msgid "rich text" msgstr "Obszar tekstowy (WYSIWYG)" -#: content/richtext/models.py:89 +#: content/richtext/models.py:104 msgid "rich texts" msgstr "Obszary tekstowe (WYSIWYG)" -#: content/rss/models.py:21 +#: content/rss/models.py:25 msgid "" "The rss field is updated several times a day. A change in the title will " "only be visible on the home page after the next feed update." msgstr "" -#: content/rss/models.py:22 +#: content/rss/models.py:28 msgid "link" msgstr "link" -#: content/rss/models.py:23 +#: content/rss/models.py:30 msgid "pre-rendered content" msgstr "" -#: content/rss/models.py:24 +#: content/rss/models.py:32 msgid "last updated" msgstr "ostatnio aktualizowany" -#: content/rss/models.py:25 +#: content/rss/models.py:33 msgid "max. items" msgstr "maksymalna ilość" -#: content/rss/models.py:29 +#: content/rss/models.py:37 msgid "RSS feed" msgstr "kanał RSS" -#: content/rss/models.py:30 +#: content/rss/models.py:38 msgid "RSS feeds" msgstr "kanały RSS" -#: content/section/models.py:41 +#: content/section/models.py:43 msgid "section" msgstr "sekcja" -#: content/section/models.py:42 +#: content/section/models.py:44 msgid "sections" msgstr "sekcje" -#: content/table/models.py:63 -msgid "plain" -msgstr "zwyczajny" - -#: content/table/models.py:64 -msgid "title row" -msgstr "tytuł" - -#: content/table/models.py:66 -msgid "title row and column" -msgstr "" - -#: content/table/models.py:72 -msgid "table" -msgstr "tablica" - -#: content/table/models.py:73 -msgid "tables" -msgstr "tablice" - -#: content/table/models.py:87 -msgid "data" -msgstr "data" - -#: content/template/models.py:51 +#: content/template/models.py:53 msgid "template content" msgstr "tekst szablonu" -#: content/template/models.py:52 +#: content/template/models.py:54 msgid "template contents" msgstr "teksty szablonów" -#: content/video/models.py:31 +#: content/template/models.py:63 models.py:400 +msgid "template" +msgstr "szablon" + +#: content/video/models.py:34 msgid "video link" msgstr "link video" -#: content/video/models.py:32 +#: content/video/models.py:36 msgid "" -"This should be a link to a youtube or vimeo video, i.e.: " -"http://www.youtube.com/watch?v=zmj1rpzDRZ0" -msgstr "Umieść link do youtube.com lub vimeo.com, np. http://www.youtube.com/watch?v=zmj1rpzDRZ0" +"This should be a link to a youtube or vimeo video, i.e.: http://www.youtube." +"com/watch?v=zmj1rpzDRZ0" +msgstr "" +"Umieść link do youtube.com lub vimeo.com, np. http://www.youtube.com/watch?" +"v=zmj1rpzDRZ0" -#: content/video/models.py:37 +#: content/video/models.py:41 msgid "video" msgstr "video" -#: content/video/models.py:38 +#: content/video/models.py:42 msgid "videos" msgstr "video" -#: contrib/tagging.py:117 +#: contrib/tagging.py:132 msgid "Tagging" msgstr "" -#: module/blog/models.py:28 +#: models.py:550 +msgid "ordering" +msgstr "sortowanie" + +#: module/blog/extensions/tags.py:17 +msgid "tags" +msgstr "tagi" + +#: module/blog/extensions/translations.py:25 +#: module/extensions/translations.py:140 translations.py:282 +msgid "language" +msgstr "język" + +#: module/blog/extensions/translations.py:35 +#: module/extensions/translations.py:148 +msgid "translation of" +msgstr "tłumaczenie" + +#: module/blog/extensions/translations.py:39 +#: module/extensions/translations.py:152 +msgid "Leave this empty for entries in the primary language." +msgstr "" + +#: module/blog/extensions/translations.py:67 +#: templates/admin/feincms/item_editor.html:33 +msgid "available translations" +msgstr "dostępne tłumaczenia" + +#: module/blog/models.py:33 msgid "published" msgstr "opublikowany" -#: module/blog/models.py:30 +#: module/blog/models.py:36 msgid "This is used for the generated navigation too." msgstr "" -#: module/blog/models.py:33 +#: module/blog/models.py:40 msgid "published on" msgstr "opublikowany na" -#: module/blog/models.py:34 -msgid "" -"Will be set automatically once you tick the `published` checkbox above." -msgstr "Zostanie automatycznie ustawione po kliknięciu \"opublikowany\" powyżej" +#: module/blog/models.py:42 +msgid "Will be set automatically once you tick the `published` checkbox above." +msgstr "" +"Zostanie automatycznie ustawione po kliknięciu \"opublikowany\" powyżej" -#: module/blog/models.py:39 +#: module/blog/models.py:48 msgid "entry" msgstr "wpis" -#: module/blog/models.py:40 +#: module/blog/models.py:49 msgid "entries" msgstr "wpisy" -#: module/blog/extensions/tags.py:12 -msgid "tags" -msgstr "tagi" - -#: module/blog/extensions/translations.py:20 -#: module/extensions/translations.py:126 -msgid "translation of" -msgstr "tłumaczenie" - -#: module/blog/extensions/translations.py:23 -#: module/extensions/translations.py:129 -msgid "Leave this empty for entries in the primary language." -msgstr "" - -#: module/blog/extensions/translations.py:44 -#: templates/admin/feincms/item_editor.html:43 -msgid "available translations" -msgstr "dostępne tłumaczenia" - -#: module/extensions/changedate.py:32 +#: module/extensions/changedate.py:41 msgid "creation date" msgstr "data utworzenia" -#: module/extensions/changedate.py:33 +#: module/extensions/changedate.py:43 msgid "modification date" msgstr "data modyfikacji" -#: module/extensions/ct_tracker.py:140 +#: module/extensions/ct_tracker.py:156 msgid "content types" msgstr "typ treści" -#: module/extensions/datepublisher.py:67 +#: module/extensions/datepublisher.py:86 msgid "publication date" msgstr "data publikacji" -#: module/extensions/datepublisher.py:70 +#: module/extensions/datepublisher.py:90 msgid "publication end date" msgstr "końcowa data publikacji" -#: module/extensions/datepublisher.py:72 +#: module/extensions/datepublisher.py:93 msgid "Leave empty if the entry should stay active forever." msgstr "Pozostaw puste jeśli nie chcesz określać końcowej daty publikacji" -#: module/extensions/datepublisher.py:103 +#: module/extensions/datepublisher.py:128 msgid "visible from - to" msgstr "widoczne od - do" -#: module/extensions/datepublisher.py:113 +#: module/extensions/datepublisher.py:139 msgid "Date-based publishing" msgstr "Określanie okresu publikacji" -#: module/extensions/featured.py:9 +#: module/extensions/featured.py:15 msgid "featured" msgstr "wyróżniony" -#: module/extensions/featured.py:14 +#: module/extensions/featured.py:21 msgid "Featured" msgstr "" -#: module/extensions/seo.py:9 +#: module/extensions/seo.py:16 msgid "meta keywords" msgstr "tagi meta - keywords" -#: module/extensions/seo.py:10 -msgid "This will be prepended to the default keyword list." -msgstr "Zostanie dołączone z przodu listy tagów" +#: module/extensions/seo.py:18 +msgid "Keywords are ignored by most search engines." +msgstr "" -#: module/extensions/seo.py:11 +#: module/extensions/seo.py:20 msgid "meta description" msgstr "tagi meta - description" -#: module/extensions/seo.py:12 -msgid "This will be prepended to the default description." -msgstr "Zostanie dołączone z przodu listy tagów" +#: module/extensions/seo.py:22 +msgid "" +"This text is displayed on the search results page. It is however not used " +"for the SEO ranking. Text longer than 140 characters is truncated." +msgstr "" -#: module/extensions/seo.py:17 +#: module/extensions/seo.py:32 msgid "Search engine optimization" msgstr "Pola SEO " -#: module/extensions/translations.py:207 +#: module/extensions/translations.py:249 msgid "Edit translation" msgstr "Edytuj tłumaczenie" -#: module/extensions/translations.py:210 +#: module/extensions/translations.py:256 msgid "Create translation" msgstr "Stwórz tłumaczenie" -#: module/extensions/translations.py:215 +#: module/extensions/translations.py:264 msgid "translations" msgstr "tłumaczenia" -#: module/medialibrary/forms.py:26 +#: module/medialibrary/forms.py:31 msgid "This would create a loop in the hierarchy" msgstr "" -#: module/medialibrary/forms.py:64 +#: module/medialibrary/forms.py:77 #, python-format msgid "" "Cannot overwrite with different file type (attempt to overwrite a " "%(old_ext)s with a %(new_ext)s)" msgstr "" -#: module/medialibrary/modeladmins.py:58 +#: module/medialibrary/modeladmins.py:63 #, python-format msgid "Successfully added %(count)d media file to %(category)s." msgid_plural "Successfully added %(count)d media files to %(category)s." @@ -448,298 +439,328 @@ msgstr[0] "" msgstr[1] "" msgstr[2] "" -#: module/medialibrary/modeladmins.py:77 +#: module/medialibrary/modeladmins.py:84 msgid "Add selected media files to category" msgstr "Dodaj zaznaczone pliki do kategorii" -#: module/medialibrary/modeladmins.py:86 +#: module/medialibrary/modeladmins.py:94 #, python-format msgid "ZIP file exported as %s" msgstr "" -#: module/medialibrary/modeladmins.py:88 +#: module/medialibrary/modeladmins.py:96 #, python-format msgid "ZIP file export failed: %s" msgstr "" -#: module/medialibrary/modeladmins.py:93 +#: module/medialibrary/modeladmins.py:104 msgid "Export selected media files as zip file" msgstr "" -#: module/medialibrary/modeladmins.py:136 +#: module/medialibrary/modeladmins.py:157 #: templates/admin/feincms/page/page/item_editor.html:15 msgid "Preview" msgstr "Podgląd" -#: module/medialibrary/modeladmins.py:141 module/medialibrary/models.py:88 +#: module/medialibrary/modeladmins.py:162 module/medialibrary/models.py:103 msgid "file size" msgstr "rozmiar pliku" -#: module/medialibrary/modeladmins.py:146 module/medialibrary/models.py:86 +#: module/medialibrary/modeladmins.py:167 module/medialibrary/models.py:100 msgid "created" msgstr "utworzony" -#: module/medialibrary/modeladmins.py:165 module/medialibrary/models.py:85 +#: module/medialibrary/modeladmins.py:187 module/medialibrary/models.py:97 msgid "file type" msgstr "typ pliku" -#: module/medialibrary/modeladmins.py:186 +#: module/medialibrary/modeladmins.py:211 msgid "file info" msgstr "informacje o pliku" -#: module/medialibrary/modeladmins.py:198 +#: module/medialibrary/modeladmins.py:224 #, python-format msgid "%d files imported" msgstr "zaimportowano %d plików" -#: module/medialibrary/modeladmins.py:200 +#: module/medialibrary/modeladmins.py:226 #, python-format msgid "ZIP import failed: %s" msgstr "" -#: module/medialibrary/modeladmins.py:202 +#: module/medialibrary/modeladmins.py:228 msgid "No input file given" msgstr "Brak wybranego pliku " -#: module/medialibrary/models.py:43 +#: module/medialibrary/models.py:49 msgid "parent" msgstr "rodzic" -#: module/medialibrary/models.py:45 module/page/models.py:171 +#: module/medialibrary/models.py:51 module/page/models.py:175 msgid "slug" msgstr "slug (wyświetlany adres)" -#: module/medialibrary/models.py:49 +#: module/medialibrary/models.py:55 msgid "category" msgstr "kategoria" -#: module/medialibrary/models.py:50 module/medialibrary/models.py:90 +#: module/medialibrary/models.py:56 module/medialibrary/models.py:106 msgid "categories" msgstr "kategorie" -#: module/medialibrary/models.py:87 +#: module/medialibrary/models.py:101 msgid "copyright" msgstr "copyright" -#: module/medialibrary/models.py:202 +#: module/medialibrary/models.py:219 msgid "Image" msgstr "Zdjęcie" -#: module/medialibrary/models.py:203 +#: module/medialibrary/models.py:221 msgid "Video" msgstr "Video" -#: module/medialibrary/models.py:204 +#: module/medialibrary/models.py:224 msgid "Audio" msgstr "Audio" -#: module/medialibrary/models.py:205 +#: module/medialibrary/models.py:226 msgid "PDF document" msgstr "Dokument PDF" -#: module/medialibrary/models.py:206 +#: module/medialibrary/models.py:227 msgid "Flash" msgstr "Flash" -#: module/medialibrary/models.py:207 +#: module/medialibrary/models.py:228 msgid "Text" msgstr "Tekst" -#: module/medialibrary/models.py:208 +#: module/medialibrary/models.py:229 msgid "Rich Text" msgstr "Obszar tekstowy (WYSIWYG)" -#: module/medialibrary/models.py:209 +#: module/medialibrary/models.py:230 msgid "Zip archive" msgstr "Archiwum ZIP" -#: module/medialibrary/models.py:210 +#: module/medialibrary/models.py:231 msgid "Microsoft Word" msgstr "Microsoft Word" -#: module/medialibrary/models.py:211 +#: module/medialibrary/models.py:233 msgid "Microsoft Excel" msgstr "Microsoft Excel" -#: module/medialibrary/models.py:212 +#: module/medialibrary/models.py:235 msgid "Microsoft PowerPoint" msgstr "Microsoft PowerPoint" -#: module/medialibrary/models.py:213 +#: module/medialibrary/models.py:237 msgid "Binary" msgstr "Binarny" -#: module/medialibrary/models.py:232 +#: module/medialibrary/models.py:261 msgid "description" msgstr "opis" -#: module/medialibrary/models.py:235 +#: module/medialibrary/models.py:264 msgid "media file translation" msgstr "" -#: module/medialibrary/models.py:236 +#: module/medialibrary/models.py:265 msgid "media file translations" msgstr "" -#: module/page/forms.py:172 +#: module/page/extensions/excerpt.py:18 +msgid "excerpt" +msgstr "" + +#: module/page/extensions/excerpt.py:21 +msgid "Add a brief excerpt summarizing the content of this page." +msgstr "" + +#: module/page/extensions/excerpt.py:25 +msgid "Excerpt" +msgstr "" + +#: module/page/extensions/navigation.py:86 +#: module/page/extensions/navigation.py:115 +msgid "navigation extension" +msgstr "" + +#: module/page/extensions/navigation.py:119 +msgid "" +"Select the module providing subpages for this page if you need to customize " +"the navigation." +msgstr "" + +#: module/page/extensions/navigation.py:134 +msgid "Navigation extension" +msgstr "" + +#: module/page/extensions/navigationgroups.py:20 +msgid "Default" +msgstr "" + +#: module/page/extensions/navigationgroups.py:21 +msgid "Footer" +msgstr "" + +#: module/page/extensions/navigationgroups.py:28 +#, fuzzy +#| msgid "in navigation" +msgid "navigation group" +msgstr "W menu" + +#: module/page/extensions/relatedpages.py:21 +msgid "Select pages that should be listed as related content." +msgstr "" + +#: module/page/extensions/relatedpages.py:26 +msgid "Related pages" +msgstr "Powiązane strony" + +#: module/page/extensions/sites.py:21 +msgid "Site" +msgstr "Strony" + +#: module/page/extensions/symlinks.py:22 +msgid "symlinked page" +msgstr "dowiązana strona" + +#: module/page/extensions/symlinks.py:23 +msgid "All content is inherited from this page if given." +msgstr "" + +#: module/page/extensions/titles.py:19 +msgid "content title" +msgstr "Tytuł treści" + +#: module/page/extensions/titles.py:22 +msgid "The first line is the main title, the following lines are subtitles." +msgstr "" + +#: module/page/extensions/titles.py:26 +msgid "page title" +msgstr "Tytuł strony" + +#: module/page/extensions/titles.py:30 +msgid "" +"Page title for browser window. Same as title bydefault. Must not be longer " +"than 70 characters." +msgstr "" + +#: module/page/extensions/titles.py:60 +msgid "Titles" +msgstr "Tytuły" + +#: module/page/forms.py:187 msgid "This URL is already taken by an active page." msgstr "ten URL jest już używany przez aktywną stronę." -#: module/page/forms.py:190 +#: module/page/forms.py:206 msgid "This URL is already taken by another active page." msgstr "ten URL jest już używany przez inną aktywną stronę." -#: module/page/modeladmins.py:41 +#: module/page/forms.py:211 +msgid "This page does not allow attachment of child pages" +msgstr "" + +#: module/page/modeladmins.py:42 msgid "Other options" msgstr "Pozostałe opcje" -#: module/page/modeladmins.py:90 module/page/models.py:174 +#: module/page/modeladmins.py:80 module/page/models.py:182 msgid "in navigation" msgstr "W menu" -#: module/page/modeladmins.py:105 +#: module/page/modeladmins.py:109 module/page/modeladmins.py:111 msgid "Add child page" msgstr "Dodaj podstronę" -#: module/page/modeladmins.py:107 +#: module/page/modeladmins.py:120 module/page/modeladmins.py:122 #: templates/admin/feincms/content_inline.html:9 msgid "View on site" msgstr "Zobacz podgląd strony" -#: module/page/modeladmins.py:126 +#: module/page/modeladmins.py:142 #, python-format -msgid "Add %(language)s Translation of \"%(page)s\"" +msgid "Add %(language)s translation of \"%(page)s\"" msgstr "" -#: module/page/modeladmins.py:156 +#: module/page/modeladmins.py:177 msgid "" "The content from the original translation has been copied to the newly " "created page." msgstr "" -#: module/page/modeladmins.py:170 +#: module/page/modeladmins.py:197 msgid "You don't have the necessary permissions to edit this object" msgstr "Nie masz wystarczających uprawnień aby edytować ten element" -#: module/page/modeladmins.py:185 +#: module/page/modeladmins.py:223 msgid "inherited" msgstr "dziedziczone" -#: module/page/modeladmins.py:189 +#: module/page/modeladmins.py:229 msgid "extensions" msgstr "rozszerzenia" -#: module/page/modeladmins.py:193 module/page/models.py:206 +#: module/page/modeladmins.py:233 module/page/models.py:221 msgid "is active" msgstr "czy jest aktywne" -#: module/page/models.py:167 +#: module/page/models.py:169 msgid "active" msgstr "aktywny" -#: module/page/models.py:175 +#: module/page/models.py:173 +msgid "This title is also used for navigation menu items." +msgstr "" + +#: module/page/models.py:176 +msgid "This is used to build the URL for this page" +msgstr "" + +#: module/page/models.py:184 msgid "override URL" msgstr "nadpisz URL" -#: module/page/models.py:176 +#: module/page/models.py:186 msgid "" "Override the target URL. Be sure to include slashes at the beginning and at " -"the end if it is a local URL. This affects both the navigation and subpages'" -" URLs." +"the end if it is a local URL. This affects both the navigation and subpages' " +"URLs." msgstr "" -#: module/page/models.py:177 +#: module/page/models.py:190 msgid "redirect to" msgstr "przekieruj do" -#: module/page/models.py:178 +#: module/page/models.py:193 msgid "Target URL for automatic redirects or the primary key of a page." msgstr "" -#: module/page/models.py:180 +#: module/page/models.py:196 msgid "Cached URL" msgstr "" -#: module/page/models.py:395 +#: module/page/models.py:298 +#, python-format +msgid "" +"This %(page_class)s uses a singleton template, and " +"FEINCMS_SINGLETON_TEMPLATE_DELETION_ALLOWED=False" +msgstr "" + +#: module/page/models.py:426 msgid "page" msgstr "strona" -#: module/page/models.py:396 +#: module/page/models.py:427 msgid "pages" msgstr "strony" -#: module/page/extensions/excerpt.py:9 -msgid "excerpt" -msgstr "" - -#: module/page/extensions/excerpt.py:10 -msgid "Add a brief excerpt summarizing the content of this page." -msgstr "" - -#: module/page/extensions/excerpt.py:12 -msgid "Excerpt" -msgstr "" - -#: module/page/extensions/navigation.py:82 -#: module/page/extensions/navigation.py:107 -msgid "navigation extension" -msgstr "" - -#: module/page/extensions/navigation.py:110 -msgid "" -"Select the module providing subpages for this page if you need to customize " -"the navigation." -msgstr "" - -#: module/page/extensions/navigation.py:125 -msgid "Navigation extension" -msgstr "" - -#: module/page/extensions/relatedpages.py:14 -msgid "Select pages that should be listed as related content." -msgstr "" - -#: module/page/extensions/relatedpages.py:19 -msgid "Related pages" -msgstr "Powiązane strony" - -#: module/page/extensions/sites.py:16 -msgid "Site" -msgstr "Strony" - -#: module/page/extensions/symlinks.py:15 -msgid "symlinked page" -msgstr "dowiązana strona" - -#: module/page/extensions/symlinks.py:16 -msgid "All content is inherited from this page if given." -msgstr "" - -#: module/page/extensions/titles.py:13 -msgid "content title" -msgstr "Tytuł treści" - -#: module/page/extensions/titles.py:14 -msgid "The first line is the main title, the following lines are subtitles." -msgstr "" - -#: module/page/extensions/titles.py:15 -msgid "page title" -msgstr "Tytuł strony" - -#: module/page/extensions/titles.py:16 -msgid "Page title for browser window. Same as title by default." -msgstr "" - -#: module/page/extensions/titles.py:43 -msgid "Titles" -msgstr "Tytuły" - -#: templates/admin/filter.html:3 -#, python-format -msgid " By %(filter_title)s " -msgstr "Po %(filter_title)s " - #: templates/admin/feincms/_messages_js.html:4 msgid "Really delete item?" msgstr "Usunąć?" @@ -758,7 +779,9 @@ msgstr "Brak możliwości usunięcia elementu" #: templates/admin/feincms/_messages_js.html:6 msgid "Cannot delete item, because it is parent of at least one other item." -msgstr "Element nie może zostać usunięty ponieważ zawiera przynajmniej jeden element podrzędny. Usuń wpierw elementy podrzędne." +msgstr "" +"Element nie może zostać usunięty ponieważ zawiera przynajmniej jeden element " +"podrzędny. Usuń wpierw elementy podrzędne." #: templates/admin/feincms/_messages_js.html:7 msgid "Change template" @@ -771,9 +794,8 @@ msgstr "Zmienić szablon?
    Wszystkie zmiany zostały zachowane." #: templates/admin/feincms/_messages_js.html:9 #, python-format msgid "" -"Really change template?
    All changes are saved and content from " -"%%(source_regions)s is moved to " -"%%(target_region)s." +"Really change template?
    All changes are saved and content from " +"%%(source_regions)s is moved to %%(target_region)s." msgstr "" #: templates/admin/feincms/_messages_js.html:12 @@ -808,22 +830,24 @@ msgstr "Pusty region" msgid "" "Content from the parent site is automatically inherited. To override this " "behaviour, add some content." -msgstr "Treść ze strony nadrzędnej został automatycznie odziedziczony. Wstaw treść aby nadpisać odziedziczoną wartość." +msgstr "" +"Treść ze strony nadrzędnej został automatycznie odziedziczony. Wstaw treść " +"aby nadpisać odziedziczoną wartość." #: templates/admin/feincms/content_editor.html:33 msgid "Add new item" msgstr "Dodaj nowy element" -#: templates/admin/feincms/content_inline.html:91 +#: templates/admin/feincms/content_inline.html:93 #, python-format msgid "Add another %(verbose_name)s" msgstr "Dodaj następny %(verbose_name)s" -#: templates/admin/feincms/content_inline.html:94 +#: templates/admin/feincms/content_inline.html:96 msgid "Remove" msgstr "Usuń" -#: templates/admin/feincms/fe_editor.html:40 +#: templates/admin/feincms/fe_editor.html:30 msgid "Save" msgstr "Zachowaj" @@ -851,13 +875,21 @@ msgstr "dół" msgid "remove" msgstr "usuń" -#: templates/admin/feincms/recover_form.html:8 -#: templates/admin/feincms/revision_form.html:8 +#: templates/admin/feincms/page/page/item_editor.html:10 +msgid "Edit on site" +msgstr "Edytuj na stronie" + #: templates/admin/feincms/page/page/item_editor.html:23 #: templates/admin/feincms/page/page/tree_editor.html:7 +#: templates/admin/feincms/recover_form.html:8 +#: templates/admin/feincms/revision_form.html:8 msgid "Home" msgstr "Główna" +#: templates/admin/feincms/page/page/item_editor.html:27 +msgid "Add" +msgstr "Dodaj" + #: templates/admin/feincms/recover_form.html:11 #, python-format msgid "Recover deleted %(verbose_name)s" @@ -880,29 +912,26 @@ msgstr "" msgid "Press the save button below to revert to this version of the object." msgstr "" -#: templates/admin/feincms/tree_editor.html:32 +#: templates/admin/feincms/tree_editor.html:22 msgid "Shortcuts" msgstr "" -#: templates/admin/feincms/tree_editor.html:34 +#: templates/admin/feincms/tree_editor.html:24 msgid "Collapse tree" msgstr "Zwiń drzewo" -#: templates/admin/feincms/tree_editor.html:35 +#: templates/admin/feincms/tree_editor.html:25 msgid "Expand tree" msgstr "Rozwiń drzewo" -#: templates/admin/feincms/tree_editor.html:38 +#: templates/admin/feincms/tree_editor.html:29 msgid "Filter" msgstr "Filtruj" -#: templates/admin/feincms/page/page/item_editor.html:10 -msgid "Edit on site" -msgstr "Edytuj na stronie" - -#: templates/admin/feincms/page/page/item_editor.html:27 -msgid "Add" -msgstr "Dodaj" +#: templates/admin/filter.html:3 +#, python-format +msgid " By %(filter_title)s " +msgstr "Po %(filter_title)s " #: templates/admin/medialibrary/add_to_category.html:5 #: templates/admin/medialibrary/add_to_category.html:9 @@ -946,7 +975,8 @@ msgstr "" #, python-format msgid "" "\n" -" %(comment_username)s said on %(comment_submit_date)s
    \n" +" %(comment_username)s said on %(comment_submit_date)s
    \n" " " msgstr "" @@ -965,3 +995,24 @@ msgstr "Wyślij" #: templates/content/contactform/thanks.html:3 msgid "Thanks!" msgstr "Dziękuję!" + +#~ msgid "plain" +#~ msgstr "zwyczajny" + +#~ msgid "title row" +#~ msgstr "tytuł" + +#~ msgid "table" +#~ msgstr "tablica" + +#~ msgid "tables" +#~ msgstr "tablice" + +#~ msgid "data" +#~ msgstr "data" + +#~ msgid "This will be prepended to the default keyword list." +#~ msgstr "Zostanie dołączone z przodu listy tagów" + +#~ msgid "This will be prepended to the default description." +#~ msgstr "Zostanie dołączone z przodu listy tagów" diff --git a/feincms/locale/pt/LC_MESSAGES/django.po b/feincms/locale/pt/LC_MESSAGES/django.po index 72e91b7cb..46674dcf0 100644 --- a/feincms/locale/pt/LC_MESSAGES/django.po +++ b/feincms/locale/pt/LC_MESSAGES/django.po @@ -1,744 +1,786 @@ # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. -# +# # Translators: # Vítor Figueiró , 2012 msgid "" msgstr "" "Project-Id-Version: FeinCMS\n" -"Report-Msgid-Bugs-To: http://github.com/feincms/feincms/issues\n" -"POT-Creation-Date: 2013-10-25 11:12+0200\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2014-07-20 14:59+0200\n" "PO-Revision-Date: 2013-10-25 09:15+0000\n" "Last-Translator: Matthias Kestenholz \n" -"Language-Team: Portuguese (http://www.transifex.com/projects/p/feincms/language/pt/)\n" +"Language-Team: Portuguese (http://www.transifex.com/projects/p/feincms/" +"language/pt/)\n" +"Language: pt\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Language: pt\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: models.py:370 content/template/models.py:59 -msgid "template" -msgstr "modelo" - -#: models.py:510 -msgid "ordering" -msgstr "ordenação" - -#: translations.py:259 module/blog/extensions/translations.py:17 -#: module/extensions/translations.py:123 -msgid "language" -msgstr "idioma" - -#: admin/filterspecs.py:35 admin/filterspecs.py:70 +#: admin/filterspecs.py:47 admin/filterspecs.py:86 msgid "All" msgstr "Todos" -#: admin/filterspecs.py:46 module/page/models.py:172 +#: admin/filterspecs.py:58 module/page/models.py:178 msgid "Parent" msgstr "Ascendente" -#: admin/filterspecs.py:81 +#: admin/filterspecs.py:97 #: templates/admin/medialibrary/mediafile/change_list.html:14 msgid "Category" msgstr "Categoria" -#: admin/item_editor.py:159 +#: admin/item_editor.py:190 #, python-format msgid "Change %s" msgstr "Modificar %s" -#: admin/tree_editor.py:234 content/rss/models.py:20 -#: content/section/models.py:34 module/blog/models.py:29 -#: module/medialibrary/models.py:40 module/page/models.py:170 -#: module/page/models.py:226 +#: admin/tree_editor.py:294 content/rss/models.py:23 +#: content/section/models.py:35 module/blog/models.py:35 +#: module/medialibrary/models.py:45 module/page/models.py:172 +#: module/page/models.py:240 msgid "title" msgstr "título" -#: admin/tree_editor.py:407 +#: admin/tree_editor.py:353 admin/tree_editor.py:370 +#, fuzzy +#| msgid "You don't have the necessary permissions to edit this object" +msgid "You do not have permission to modify this object" +msgstr "Não possui as permissões necessárias para editar este objecto" + +#: admin/tree_editor.py:491 +msgid "No permission" +msgstr "" + +#: admin/tree_editor.py:509 #, python-format msgid "%s has been moved to a new position." msgstr "%s foi movido para uma nova posição" -#: admin/tree_editor.py:411 +#: admin/tree_editor.py:512 msgid "Did not understand moving instruction." msgstr "A instrução para mover não foi entendida." -#: admin/tree_editor.py:420 +#: admin/tree_editor.py:523 msgid "actions" msgstr "acções" -#: admin/tree_editor.py:432 -#, python-format -msgid "Successfully deleted %s items." -msgstr "" +#: admin/tree_editor.py:552 +#, fuzzy, python-format +#| msgid "Successfully added %(count)d media file to %(category)s." +#| msgid_plural "Successfully added %(count)d media files to %(category)s." +msgid "Successfully deleted %(count)d items." +msgstr "%(count)d ficheiro multimédia adicionado com sucesso a %(category)s." -#: admin/tree_editor.py:440 +#: admin/tree_editor.py:565 #, python-format msgid "Delete selected %(verbose_name_plural)s" msgstr "" -#: content/application/models.py:218 +#: content/application/models.py:147 msgid "application content" msgstr "conteúdo de aplicação" -#: content/application/models.py:219 +#: content/application/models.py:148 msgid "application contents" msgstr "conteúdos de aplicação" -#: content/application/models.py:249 +#: content/application/models.py:179 msgid "application" msgstr "aplicação" -#: content/comments/models.py:24 +#: content/comments/models.py:32 msgid "enabled" msgstr "habilitado" -#: content/comments/models.py:24 +#: content/comments/models.py:33 msgid "New comments may be added" msgstr "Podem ser adicionados novos comentários" -#: content/comments/models.py:28 content/comments/models.py:29 +#: content/comments/models.py:37 content/comments/models.py:38 msgid "comments" msgstr "comentários" -#: content/comments/models.py:49 +#: content/comments/models.py:60 msgid "public" msgstr "público" -#: content/comments/models.py:49 +#: content/comments/models.py:61 msgid "not public" msgstr "não público" -#: content/contactform/models.py:18 +#: content/contactform/models.py:20 msgid "name" msgstr "nome" -#: content/contactform/models.py:19 +#: content/contactform/models.py:21 msgid "email" msgstr "e-mail" -#: content/contactform/models.py:20 +#: content/contactform/models.py:22 msgid "subject" msgstr "assunto" -#: content/contactform/models.py:23 content/raw/models.py:14 +#: content/contactform/models.py:26 content/raw/models.py:16 msgid "content" msgstr "conteúdo" -#: content/contactform/models.py:34 +#: content/contactform/models.py:37 msgid "contact form" msgstr "formulário de contacto" -#: content/contactform/models.py:35 +#: content/contactform/models.py:38 msgid "contact forms" msgstr "formulários de contacto" -#: content/file/models.py:20 content/file/models.py:25 -#: module/medialibrary/models.py:84 +#: content/file/models.py:23 content/file/models.py:28 +#: module/medialibrary/models.py:94 msgid "file" msgstr "ficheiro" -#: content/file/models.py:26 +#: content/file/models.py:29 msgid "files" msgstr "ficheiros" -#: content/image/models.py:45 content/image/models.py:54 +#: content/image/models.py:46 content/image/models.py:55 msgid "image" msgstr "imagem" -#: content/image/models.py:48 +#: content/image/models.py:49 msgid "alternate text" msgstr "texto alternativo" -#: content/image/models.py:49 +#: content/image/models.py:50 msgid "Description of image" msgstr "Descrição da imagem" -#: content/image/models.py:50 module/medialibrary/models.py:231 +#: content/image/models.py:51 module/medialibrary/models.py:260 msgid "caption" msgstr "legenda" -#: content/image/models.py:55 +#: content/image/models.py:56 msgid "images" msgstr "imagens" -#: content/image/models.py:80 +#: content/image/models.py:82 msgid "position" msgstr "posição" -#: content/image/models.py:88 +#: content/image/models.py:90 msgid "format" msgstr "formato" -#: content/medialibrary/models.py:49 content/section/models.py:36 -#: module/medialibrary/fields.py:59 module/medialibrary/models.py:97 +#: content/medialibrary/models.py:51 content/section/models.py:38 +#: module/medialibrary/fields.py:66 module/medialibrary/models.py:112 msgid "media file" msgstr "ficheiro multimédia" -#: content/medialibrary/models.py:50 module/medialibrary/models.py:98 +#: content/medialibrary/models.py:52 module/medialibrary/models.py:113 msgid "media files" msgstr "ficheiros multimédia" -#: content/medialibrary/models.py:58 content/section/models.py:52 -#: content/table/models.py:82 +#: content/medialibrary/models.py:64 content/section/models.py:59 msgid "type" msgstr "tipo" -#: content/raw/models.py:18 +#: content/raw/models.py:20 msgid "raw content" msgstr "conteúdo cru" -#: content/raw/models.py:19 +#: content/raw/models.py:21 msgid "raw contents" msgstr "conteúdos crus" -#: content/richtext/models.py:22 +#: content/richtext/models.py:24 msgid "HTML Tidy" msgstr "HTML Tidy" -#: content/richtext/models.py:23 +#: content/richtext/models.py:25 msgid "Ignore the HTML validation warnings" msgstr "Ignorar os avisos de validação do HTML" -#: content/richtext/models.py:47 +#: content/richtext/models.py:55 #, python-format msgid "" "HTML validation produced %(count)d warnings. Please review the updated " "content below before continuing: %(messages)s" -msgstr "A validação do HTML produziu %(count)d avisos. Por favor reveja o conteúdo actualizado em baixo antes de continuar: %(messages)s " +msgstr "" +"A validação do HTML produziu %(count)d avisos. Por favor reveja o conteúdo " +"actualizado em baixo antes de continuar: %(messages)s " -#: content/richtext/models.py:84 content/section/models.py:35 +#: content/richtext/models.py:99 content/section/models.py:36 msgid "text" msgstr "texto" -#: content/richtext/models.py:88 +#: content/richtext/models.py:103 msgid "rich text" msgstr "texto rico" -#: content/richtext/models.py:89 +#: content/richtext/models.py:104 msgid "rich texts" msgstr "textos ricos" -#: content/rss/models.py:21 +#: content/rss/models.py:25 msgid "" "The rss field is updated several times a day. A change in the title will " "only be visible on the home page after the next feed update." -msgstr "O feed RSS é actualizado várias vezes ao dia. Uma alteração do título só aparece no site após a actualização do feed RSS seguinte." +msgstr "" +"O feed RSS é actualizado várias vezes ao dia. Uma alteração do título só " +"aparece no site após a actualização do feed RSS seguinte." -#: content/rss/models.py:22 +#: content/rss/models.py:28 msgid "link" msgstr "ligação" -#: content/rss/models.py:23 +#: content/rss/models.py:30 msgid "pre-rendered content" msgstr "conteúdo pré-renderizado" -#: content/rss/models.py:24 +#: content/rss/models.py:32 msgid "last updated" msgstr "última actualização" -#: content/rss/models.py:25 +#: content/rss/models.py:33 msgid "max. items" msgstr "número máximo" -#: content/rss/models.py:29 +#: content/rss/models.py:37 msgid "RSS feed" msgstr "feed RSS" -#: content/rss/models.py:30 +#: content/rss/models.py:38 msgid "RSS feeds" msgstr "feed RSS" -#: content/section/models.py:41 +#: content/section/models.py:43 msgid "section" msgstr "secção" -#: content/section/models.py:42 +#: content/section/models.py:44 msgid "sections" msgstr "secções" -#: content/table/models.py:63 -msgid "plain" -msgstr "simples" - -#: content/table/models.py:64 -msgid "title row" -msgstr "linha de título" - -#: content/table/models.py:66 -msgid "title row and column" -msgstr "linha e coluna de título" - -#: content/table/models.py:72 -msgid "table" -msgstr "tabela" - -#: content/table/models.py:73 -msgid "tables" -msgstr "tabelas" - -#: content/table/models.py:87 -msgid "data" -msgstr "dados" - -#: content/template/models.py:51 +#: content/template/models.py:53 msgid "template content" msgstr "conteúdo do modelo" -#: content/template/models.py:52 +#: content/template/models.py:54 msgid "template contents" msgstr "conteúdos de template" -#: content/video/models.py:31 +#: content/template/models.py:63 models.py:400 +msgid "template" +msgstr "modelo" + +#: content/video/models.py:34 msgid "video link" msgstr "ligação de vídeo" -#: content/video/models.py:32 +#: content/video/models.py:36 msgid "" -"This should be a link to a youtube or vimeo video, i.e.: " +"This should be a link to a youtube or vimeo video, i.e.: http://www.youtube." +"com/watch?v=zmj1rpzDRZ0" +msgstr "" +"Isto deve ser uma ligação para um vídeo do Youtube ou do vimeo, p.ex.: " "http://www.youtube.com/watch?v=zmj1rpzDRZ0" -msgstr "Isto deve ser uma ligação para um vídeo do Youtube ou do vimeo, p.ex.: http://www.youtube.com/watch?v=zmj1rpzDRZ0" -#: content/video/models.py:37 +#: content/video/models.py:41 msgid "video" msgstr "vídeo" -#: content/video/models.py:38 +#: content/video/models.py:42 msgid "videos" msgstr "vídeos" -#: contrib/tagging.py:117 +#: contrib/tagging.py:132 msgid "Tagging" msgstr "Tagging" -#: module/blog/models.py:28 +#: models.py:550 +msgid "ordering" +msgstr "ordenação" + +#: module/blog/extensions/tags.py:17 +msgid "tags" +msgstr "etiquetas" + +#: module/blog/extensions/translations.py:25 +#: module/extensions/translations.py:140 translations.py:282 +msgid "language" +msgstr "idioma" + +#: module/blog/extensions/translations.py:35 +#: module/extensions/translations.py:148 +msgid "translation of" +msgstr "tradução de" + +#: module/blog/extensions/translations.py:39 +#: module/extensions/translations.py:152 +msgid "Leave this empty for entries in the primary language." +msgstr "Deixe este campo em branco nas entradas no idioma original." + +#: module/blog/extensions/translations.py:67 +#: templates/admin/feincms/item_editor.html:33 +msgid "available translations" +msgstr "traduções disponíveis" + +#: module/blog/models.py:33 msgid "published" msgstr "publicado" -#: module/blog/models.py:30 +#: module/blog/models.py:36 msgid "This is used for the generated navigation too." msgstr "Isto também é usado para a navegação gerada automaticamente." -#: module/blog/models.py:33 +#: module/blog/models.py:40 msgid "published on" msgstr "publicado em" -#: module/blog/models.py:34 -msgid "" -"Will be set automatically once you tick the `published` checkbox above." -msgstr "Será definido automaticamente quando activar a caixa de verificação 'publicado' acima." +#: module/blog/models.py:42 +msgid "Will be set automatically once you tick the `published` checkbox above." +msgstr "" +"Será definido automaticamente quando activar a caixa de verificação " +"'publicado' acima." -#: module/blog/models.py:39 +#: module/blog/models.py:48 msgid "entry" msgstr "entrada" -#: module/blog/models.py:40 +#: module/blog/models.py:49 msgid "entries" msgstr "entradas" -#: module/blog/extensions/tags.py:12 -msgid "tags" -msgstr "etiquetas" - -#: module/blog/extensions/translations.py:20 -#: module/extensions/translations.py:126 -msgid "translation of" -msgstr "tradução de" - -#: module/blog/extensions/translations.py:23 -#: module/extensions/translations.py:129 -msgid "Leave this empty for entries in the primary language." -msgstr "Deixe este campo em branco nas entradas no idioma original." - -#: module/blog/extensions/translations.py:44 -#: templates/admin/feincms/item_editor.html:43 -msgid "available translations" -msgstr "traduções disponíveis" - -#: module/extensions/changedate.py:32 +#: module/extensions/changedate.py:41 msgid "creation date" msgstr "data de criação" -#: module/extensions/changedate.py:33 +#: module/extensions/changedate.py:43 msgid "modification date" msgstr "data de modificação" -#: module/extensions/ct_tracker.py:140 +#: module/extensions/ct_tracker.py:156 msgid "content types" msgstr "tipos de conteúdo" -#: module/extensions/datepublisher.py:67 +#: module/extensions/datepublisher.py:86 msgid "publication date" msgstr "data de publicação" -#: module/extensions/datepublisher.py:70 +#: module/extensions/datepublisher.py:90 msgid "publication end date" msgstr "publicar até" -#: module/extensions/datepublisher.py:72 +#: module/extensions/datepublisher.py:93 msgid "Leave empty if the entry should stay active forever." msgstr "Deixe vazio se a entrada deve permanecer activa para sempre." -#: module/extensions/datepublisher.py:103 +#: module/extensions/datepublisher.py:128 msgid "visible from - to" msgstr "visível de - até" -#: module/extensions/datepublisher.py:113 +#: module/extensions/datepublisher.py:139 msgid "Date-based publishing" msgstr "Publicação baseada em datas" -#: module/extensions/featured.py:9 +#: module/extensions/featured.py:15 msgid "featured" msgstr "em destaque" -#: module/extensions/featured.py:14 +#: module/extensions/featured.py:21 msgid "Featured" msgstr "Em destaque" -#: module/extensions/seo.py:9 +#: module/extensions/seo.py:16 msgid "meta keywords" msgstr "palavras-chave meta" -#: module/extensions/seo.py:10 -msgid "This will be prepended to the default keyword list." -msgstr "Isto será inserido antes da lista de palavras-chave padrão." +#: module/extensions/seo.py:18 +msgid "Keywords are ignored by most search engines." +msgstr "" -#: module/extensions/seo.py:11 +#: module/extensions/seo.py:20 msgid "meta description" msgstr "descrição meta" -#: module/extensions/seo.py:12 -msgid "This will be prepended to the default description." -msgstr "Isto será inserido antes da descrição padrão." +#: module/extensions/seo.py:22 +msgid "" +"This text is displayed on the search results page. It is however not used " +"for the SEO ranking. Text longer than 140 characters is truncated." +msgstr "" -#: module/extensions/seo.py:17 +#: module/extensions/seo.py:32 msgid "Search engine optimization" msgstr "Optimização para motor de busca" -#: module/extensions/translations.py:207 +#: module/extensions/translations.py:249 msgid "Edit translation" msgstr "Editar a tradução" -#: module/extensions/translations.py:210 +#: module/extensions/translations.py:256 msgid "Create translation" msgstr "Criar tradução" -#: module/extensions/translations.py:215 +#: module/extensions/translations.py:264 msgid "translations" msgstr "traduções" -#: module/medialibrary/forms.py:26 +#: module/medialibrary/forms.py:31 msgid "This would create a loop in the hierarchy" msgstr "Isto criaria um ciclo na hierarquia" -#: module/medialibrary/forms.py:64 +#: module/medialibrary/forms.py:77 #, python-format msgid "" "Cannot overwrite with different file type (attempt to overwrite a " "%(old_ext)s with a %(new_ext)s)" -msgstr "Não é possível sobrescrever com um tipo de ficheiro diferente (tentativa de substituir um %(old_ext)s com um %(new_ext)s)" +msgstr "" +"Não é possível sobrescrever com um tipo de ficheiro diferente (tentativa de " +"substituir um %(old_ext)s com um %(new_ext)s)" -#: module/medialibrary/modeladmins.py:58 +#: module/medialibrary/modeladmins.py:63 #, python-format msgid "Successfully added %(count)d media file to %(category)s." msgid_plural "Successfully added %(count)d media files to %(category)s." -msgstr[0] "%(count)d ficheiro multimédia adicionado com sucesso a %(category)s." -msgstr[1] "%(count)d ficheiros multimédia adicionados com sucesso a %(category)s." +msgstr[0] "" +"%(count)d ficheiro multimédia adicionado com sucesso a %(category)s." +msgstr[1] "" +"%(count)d ficheiros multimédia adicionados com sucesso a %(category)s." -#: module/medialibrary/modeladmins.py:77 +#: module/medialibrary/modeladmins.py:84 msgid "Add selected media files to category" msgstr "Adicionar os ficheiros multimédia seleccionados à categoria" -#: module/medialibrary/modeladmins.py:86 +#: module/medialibrary/modeladmins.py:94 #, python-format msgid "ZIP file exported as %s" msgstr "Ficheiro ZIP exportado como %s" -#: module/medialibrary/modeladmins.py:88 +#: module/medialibrary/modeladmins.py:96 #, python-format msgid "ZIP file export failed: %s" msgstr "A exportação do ficheiro ZIP falhou: %s" -#: module/medialibrary/modeladmins.py:93 +#: module/medialibrary/modeladmins.py:104 msgid "Export selected media files as zip file" msgstr "Exportar os ficheiros multimédia seleccionados como ficheiro zip" -#: module/medialibrary/modeladmins.py:136 +#: module/medialibrary/modeladmins.py:157 #: templates/admin/feincms/page/page/item_editor.html:15 msgid "Preview" msgstr "Antevisão" -#: module/medialibrary/modeladmins.py:141 module/medialibrary/models.py:88 +#: module/medialibrary/modeladmins.py:162 module/medialibrary/models.py:103 msgid "file size" msgstr "tamanho do ficheiro" -#: module/medialibrary/modeladmins.py:146 module/medialibrary/models.py:86 +#: module/medialibrary/modeladmins.py:167 module/medialibrary/models.py:100 msgid "created" msgstr "criado em" -#: module/medialibrary/modeladmins.py:165 module/medialibrary/models.py:85 +#: module/medialibrary/modeladmins.py:187 module/medialibrary/models.py:97 msgid "file type" msgstr "tipo de ficheiro" -#: module/medialibrary/modeladmins.py:186 +#: module/medialibrary/modeladmins.py:211 msgid "file info" msgstr "informação do ficheiro" -#: module/medialibrary/modeladmins.py:198 +#: module/medialibrary/modeladmins.py:224 #, python-format msgid "%d files imported" msgstr "%d ficheiros importados" -#: module/medialibrary/modeladmins.py:200 +#: module/medialibrary/modeladmins.py:226 #, python-format msgid "ZIP import failed: %s" msgstr "A importação do ficheiro ZIP falhou: %s" -#: module/medialibrary/modeladmins.py:202 +#: module/medialibrary/modeladmins.py:228 msgid "No input file given" msgstr "Não indicou o ficheiro de origem" -#: module/medialibrary/models.py:43 +#: module/medialibrary/models.py:49 msgid "parent" msgstr "ascendente" -#: module/medialibrary/models.py:45 module/page/models.py:171 +#: module/medialibrary/models.py:51 module/page/models.py:175 msgid "slug" msgstr "slug" -#: module/medialibrary/models.py:49 +#: module/medialibrary/models.py:55 msgid "category" msgstr "categoria" -#: module/medialibrary/models.py:50 module/medialibrary/models.py:90 +#: module/medialibrary/models.py:56 module/medialibrary/models.py:106 msgid "categories" msgstr "categorias" -#: module/medialibrary/models.py:87 +#: module/medialibrary/models.py:101 msgid "copyright" msgstr "copyright" -#: module/medialibrary/models.py:202 +#: module/medialibrary/models.py:219 msgid "Image" msgstr "Imagem" -#: module/medialibrary/models.py:203 +#: module/medialibrary/models.py:221 msgid "Video" msgstr "Vídeo" -#: module/medialibrary/models.py:204 +#: module/medialibrary/models.py:224 msgid "Audio" msgstr "Áudio" -#: module/medialibrary/models.py:205 +#: module/medialibrary/models.py:226 msgid "PDF document" msgstr "Documento PDF" -#: module/medialibrary/models.py:206 +#: module/medialibrary/models.py:227 msgid "Flash" msgstr "Flash" -#: module/medialibrary/models.py:207 +#: module/medialibrary/models.py:228 msgid "Text" msgstr "Texto" -#: module/medialibrary/models.py:208 +#: module/medialibrary/models.py:229 msgid "Rich Text" msgstr "Texto Rico" -#: module/medialibrary/models.py:209 +#: module/medialibrary/models.py:230 msgid "Zip archive" msgstr "Ficheiro zip" -#: module/medialibrary/models.py:210 +#: module/medialibrary/models.py:231 msgid "Microsoft Word" msgstr "Microsoft Word" -#: module/medialibrary/models.py:211 +#: module/medialibrary/models.py:233 msgid "Microsoft Excel" msgstr "Microsoft Excel" -#: module/medialibrary/models.py:212 +#: module/medialibrary/models.py:235 msgid "Microsoft PowerPoint" msgstr "Microsoft PowerPoint" -#: module/medialibrary/models.py:213 +#: module/medialibrary/models.py:237 msgid "Binary" msgstr "Binário" -#: module/medialibrary/models.py:232 +#: module/medialibrary/models.py:261 msgid "description" msgstr "descrição" -#: module/medialibrary/models.py:235 +#: module/medialibrary/models.py:264 msgid "media file translation" msgstr "tradução do ficheiro multimédia" -#: module/medialibrary/models.py:236 +#: module/medialibrary/models.py:265 msgid "media file translations" msgstr "traduções do ficheiro multimédia" -#: module/page/forms.py:172 +#: module/page/extensions/excerpt.py:18 +msgid "excerpt" +msgstr "excerto" + +#: module/page/extensions/excerpt.py:21 +msgid "Add a brief excerpt summarizing the content of this page." +msgstr "Adicione um breve excerto que resuma o conteúdo desta página." + +#: module/page/extensions/excerpt.py:25 +msgid "Excerpt" +msgstr "Excerto" + +#: module/page/extensions/navigation.py:86 +#: module/page/extensions/navigation.py:115 +msgid "navigation extension" +msgstr "extensão de navegação" + +#: module/page/extensions/navigation.py:119 +msgid "" +"Select the module providing subpages for this page if you need to customize " +"the navigation." +msgstr "" +"Seleccione o módulo que providencia as sub-páginas, caso necessite de " +"personalizar a navegação." + +#: module/page/extensions/navigation.py:134 +msgid "Navigation extension" +msgstr "Extensão de navegação" + +#: module/page/extensions/navigationgroups.py:20 +msgid "Default" +msgstr "" + +#: module/page/extensions/navigationgroups.py:21 +msgid "Footer" +msgstr "" + +#: module/page/extensions/navigationgroups.py:28 +#, fuzzy +#| msgid "in navigation" +msgid "navigation group" +msgstr "na navegação" + +#: module/page/extensions/relatedpages.py:21 +msgid "Select pages that should be listed as related content." +msgstr "Seleccione páginas a listar como conteúdo relacionado." + +#: module/page/extensions/relatedpages.py:26 +msgid "Related pages" +msgstr "Páginas relacionadas" + +#: module/page/extensions/sites.py:21 +msgid "Site" +msgstr "Site" + +#: module/page/extensions/symlinks.py:22 +msgid "symlinked page" +msgstr "página ligada" + +#: module/page/extensions/symlinks.py:23 +msgid "All content is inherited from this page if given." +msgstr "O conteúdo é herdado desta página." + +#: module/page/extensions/titles.py:19 +msgid "content title" +msgstr "título do conteúdo" + +#: module/page/extensions/titles.py:22 +msgid "The first line is the main title, the following lines are subtitles." +msgstr "" +"A primeira linha é o título principal. Outras linhas serão sub-títulos." + +#: module/page/extensions/titles.py:26 +msgid "page title" +msgstr "título da página" + +#: module/page/extensions/titles.py:30 +#, fuzzy +#| msgid "Page title for browser window. Same as title by default." +msgid "" +"Page title for browser window. Same as title bydefault. Must not be longer " +"than 70 characters." +msgstr "" +"Título da página para a janela do navegador. Se omitido assume o título." + +#: module/page/extensions/titles.py:60 +msgid "Titles" +msgstr "Títulos" + +#: module/page/forms.py:187 msgid "This URL is already taken by an active page." msgstr "Este URL já está tomado por uma página activa." -#: module/page/forms.py:190 +#: module/page/forms.py:206 msgid "This URL is already taken by another active page." msgstr "Este URL já está tomado por uma outra página activa." -#: module/page/modeladmins.py:41 +#: module/page/forms.py:211 +msgid "This page does not allow attachment of child pages" +msgstr "" + +#: module/page/modeladmins.py:42 msgid "Other options" msgstr "outras opções" -#: module/page/modeladmins.py:90 module/page/models.py:174 +#: module/page/modeladmins.py:80 module/page/models.py:182 msgid "in navigation" msgstr "na navegação" -#: module/page/modeladmins.py:105 +#: module/page/modeladmins.py:109 module/page/modeladmins.py:111 msgid "Add child page" msgstr "Adicionar página descendente" -#: module/page/modeladmins.py:107 +#: module/page/modeladmins.py:120 module/page/modeladmins.py:122 #: templates/admin/feincms/content_inline.html:9 msgid "View on site" msgstr "Ver no site" -#: module/page/modeladmins.py:126 +#: module/page/modeladmins.py:142 #, python-format -msgid "Add %(language)s Translation of \"%(page)s\"" +msgid "Add %(language)s translation of \"%(page)s\"" msgstr "" -#: module/page/modeladmins.py:156 +#: module/page/modeladmins.py:177 msgid "" "The content from the original translation has been copied to the newly " "created page." -msgstr "O conteúdo da tradução original foi copiado para a página recém-criada." +msgstr "" +"O conteúdo da tradução original foi copiado para a página recém-criada." -#: module/page/modeladmins.py:170 +#: module/page/modeladmins.py:197 msgid "You don't have the necessary permissions to edit this object" msgstr "Não possui as permissões necessárias para editar este objecto" -#: module/page/modeladmins.py:185 +#: module/page/modeladmins.py:223 msgid "inherited" msgstr "herdado" -#: module/page/modeladmins.py:189 +#: module/page/modeladmins.py:229 msgid "extensions" msgstr "extensões" -#: module/page/modeladmins.py:193 module/page/models.py:206 +#: module/page/modeladmins.py:233 module/page/models.py:221 msgid "is active" msgstr "activo" -#: module/page/models.py:167 +#: module/page/models.py:169 msgid "active" msgstr "activo" -#: module/page/models.py:175 +#: module/page/models.py:173 +#, fuzzy +#| msgid "This is used for the generated navigation too." +msgid "This title is also used for navigation menu items." +msgstr "Isto também é usado para a navegação gerada automaticamente." + +#: module/page/models.py:176 +msgid "This is used to build the URL for this page" +msgstr "" + +#: module/page/models.py:184 msgid "override URL" msgstr "URL efectivo" -#: module/page/models.py:176 +#: module/page/models.py:186 msgid "" "Override the target URL. Be sure to include slashes at the beginning and at " -"the end if it is a local URL. This affects both the navigation and subpages'" -" URLs." -msgstr "URL efectivo. Deve conter uma / no início e no fim, caso se trate de um URL local. Este campo determina a navegação e os URL's das sub-páginas." +"the end if it is a local URL. This affects both the navigation and subpages' " +"URLs." +msgstr "" +"URL efectivo. Deve conter uma / no início e no fim, caso se trate de um URL " +"local. Este campo determina a navegação e os URL's das sub-páginas." -#: module/page/models.py:177 +#: module/page/models.py:190 msgid "redirect to" msgstr "redireccionar para" -#: module/page/models.py:178 +#: module/page/models.py:193 msgid "Target URL for automatic redirects or the primary key of a page." msgstr "" -#: module/page/models.py:180 +#: module/page/models.py:196 msgid "Cached URL" msgstr "URL em cache" -#: module/page/models.py:395 +#: module/page/models.py:298 +#, python-format +msgid "" +"This %(page_class)s uses a singleton template, and " +"FEINCMS_SINGLETON_TEMPLATE_DELETION_ALLOWED=False" +msgstr "" + +#: module/page/models.py:426 msgid "page" msgstr "página" -#: module/page/models.py:396 +#: module/page/models.py:427 msgid "pages" msgstr "páginas" -#: module/page/extensions/excerpt.py:9 -msgid "excerpt" -msgstr "excerto" - -#: module/page/extensions/excerpt.py:10 -msgid "Add a brief excerpt summarizing the content of this page." -msgstr "Adicione um breve excerto que resuma o conteúdo desta página." - -#: module/page/extensions/excerpt.py:12 -msgid "Excerpt" -msgstr "Excerto" - -#: module/page/extensions/navigation.py:82 -#: module/page/extensions/navigation.py:107 -msgid "navigation extension" -msgstr "extensão de navegação" - -#: module/page/extensions/navigation.py:110 -msgid "" -"Select the module providing subpages for this page if you need to customize " -"the navigation." -msgstr "Seleccione o módulo que providencia as sub-páginas, caso necessite de personalizar a navegação." - -#: module/page/extensions/navigation.py:125 -msgid "Navigation extension" -msgstr "Extensão de navegação" - -#: module/page/extensions/relatedpages.py:14 -msgid "Select pages that should be listed as related content." -msgstr "Seleccione páginas a listar como conteúdo relacionado." - -#: module/page/extensions/relatedpages.py:19 -msgid "Related pages" -msgstr "Páginas relacionadas" - -#: module/page/extensions/sites.py:16 -msgid "Site" -msgstr "Site" - -#: module/page/extensions/symlinks.py:15 -msgid "symlinked page" -msgstr "página ligada" - -#: module/page/extensions/symlinks.py:16 -msgid "All content is inherited from this page if given." -msgstr "O conteúdo é herdado desta página." - -#: module/page/extensions/titles.py:13 -msgid "content title" -msgstr "título do conteúdo" - -#: module/page/extensions/titles.py:14 -msgid "The first line is the main title, the following lines are subtitles." -msgstr "A primeira linha é o título principal. Outras linhas serão sub-títulos." - -#: module/page/extensions/titles.py:15 -msgid "page title" -msgstr "título da página" - -#: module/page/extensions/titles.py:16 -msgid "Page title for browser window. Same as title by default." -msgstr "Título da página para a janela do navegador. Se omitido assume o título." - -#: module/page/extensions/titles.py:43 -msgid "Titles" -msgstr "Títulos" - -#: templates/admin/filter.html:3 -#, python-format -msgid " By %(filter_title)s " -msgstr "Por %(filter_title)s" - #: templates/admin/feincms/_messages_js.html:4 msgid "Really delete item?" msgstr "Confirma a eliminação do item?" @@ -765,14 +807,14 @@ msgstr "Alterar o modelo" #: templates/admin/feincms/_messages_js.html:8 msgid "Really change template?
    All changes are saved." -msgstr "Confirma a alteração do modelo?
    Todas as alterações serão guardadas." +msgstr "" +"Confirma a alteração do modelo?
    Todas as alterações serão guardadas." #: templates/admin/feincms/_messages_js.html:9 #, python-format msgid "" -"Really change template?
    All changes are saved and content from " -"%%(source_regions)s is moved to " -"%%(target_region)s." +"Really change template?
    All changes are saved and content from " +"%%(source_regions)s is moved to %%(target_region)s." msgstr "" #: templates/admin/feincms/_messages_js.html:12 @@ -813,16 +855,16 @@ msgstr "O conteúdo é herdado da página ascendente, excepto se o indicar aqui. msgid "Add new item" msgstr "Adicionar novo item" -#: templates/admin/feincms/content_inline.html:91 +#: templates/admin/feincms/content_inline.html:93 #, python-format msgid "Add another %(verbose_name)s" msgstr "Adicionar outro %(verbose_name)s" -#: templates/admin/feincms/content_inline.html:94 +#: templates/admin/feincms/content_inline.html:96 msgid "Remove" msgstr "Remover" -#: templates/admin/feincms/fe_editor.html:40 +#: templates/admin/feincms/fe_editor.html:30 msgid "Save" msgstr "Guardar" @@ -850,13 +892,21 @@ msgstr "para baixo" msgid "remove" msgstr "remover" -#: templates/admin/feincms/recover_form.html:8 -#: templates/admin/feincms/revision_form.html:8 +#: templates/admin/feincms/page/page/item_editor.html:10 +msgid "Edit on site" +msgstr "Editar no site" + #: templates/admin/feincms/page/page/item_editor.html:23 #: templates/admin/feincms/page/page/tree_editor.html:7 +#: templates/admin/feincms/recover_form.html:8 +#: templates/admin/feincms/revision_form.html:8 msgid "Home" msgstr "Página inicial" +#: templates/admin/feincms/page/page/item_editor.html:27 +msgid "Add" +msgstr "Adicionar" + #: templates/admin/feincms/recover_form.html:11 #, python-format msgid "Recover deleted %(verbose_name)s" @@ -864,7 +914,8 @@ msgstr "Recuperar %(verbose_name)s excluído" #: templates/admin/feincms/recover_form.html:17 msgid "Press the save button below to recover this version of the object." -msgstr "Clique no botão 'Guardar' em baixo para recuperar esta versão do objecto." +msgstr "" +"Clique no botão 'Guardar' em baixo para recuperar esta versão do objecto." #: templates/admin/feincms/revision_form.html:12 msgid "History" @@ -877,31 +928,29 @@ msgstr "Reverter %(verbose_name)s" #: templates/admin/feincms/revision_form.html:24 msgid "Press the save button below to revert to this version of the object." -msgstr "Clique no botão 'Guardar' em baixo para reverter para esta versão do objecto." +msgstr "" +"Clique no botão 'Guardar' em baixo para reverter para esta versão do objecto." -#: templates/admin/feincms/tree_editor.html:32 +#: templates/admin/feincms/tree_editor.html:22 msgid "Shortcuts" msgstr "Atalhos" -#: templates/admin/feincms/tree_editor.html:34 +#: templates/admin/feincms/tree_editor.html:24 msgid "Collapse tree" msgstr "Colapsar a árvore" -#: templates/admin/feincms/tree_editor.html:35 +#: templates/admin/feincms/tree_editor.html:25 msgid "Expand tree" msgstr "Expandir a árvore" -#: templates/admin/feincms/tree_editor.html:38 +#: templates/admin/feincms/tree_editor.html:29 msgid "Filter" msgstr "Filtrar" -#: templates/admin/feincms/page/page/item_editor.html:10 -msgid "Edit on site" -msgstr "Editar no site" - -#: templates/admin/feincms/page/page/item_editor.html:27 -msgid "Add" -msgstr "Adicionar" +#: templates/admin/filter.html:3 +#, python-format +msgid " By %(filter_title)s " +msgstr "Por %(filter_title)s" #: templates/admin/medialibrary/add_to_category.html:5 #: templates/admin/medialibrary/add_to_category.html:9 @@ -914,7 +963,8 @@ msgstr "Seleccione a categoria a aplicar:" #: templates/admin/medialibrary/add_to_category.html:17 msgid "The following media files will be added to the selected category:" -msgstr "Os seguintes ficheiros multimédia serão adicionados à categoria seleccionada:" +msgstr "" +"Os seguintes ficheiros multimédia serão adicionados à categoria seleccionada:" #: templates/admin/medialibrary/add_to_category.html:22 msgid "Add to category" @@ -945,9 +995,14 @@ msgstr "%(comment_count)s comentários." #, python-format msgid "" "\n" -" %(comment_username)s said on %(comment_submit_date)s
    \n" +" %(comment_username)s said on %(comment_submit_date)s
    \n" +" " +msgstr "" +"\n" +" %(comment_username)s disse em " +"%(comment_submit_date)s
    \n" " " -msgstr "\n %(comment_username)s disse em %(comment_submit_date)s
    \n " #: templates/content/comments/default.html:28 msgid "No comments." @@ -964,3 +1019,27 @@ msgstr "Enviar" #: templates/content/contactform/thanks.html:3 msgid "Thanks!" msgstr "Obrigado!" + +#~ msgid "plain" +#~ msgstr "simples" + +#~ msgid "title row" +#~ msgstr "linha de título" + +#~ msgid "title row and column" +#~ msgstr "linha e coluna de título" + +#~ msgid "table" +#~ msgstr "tabela" + +#~ msgid "tables" +#~ msgstr "tabelas" + +#~ msgid "data" +#~ msgstr "dados" + +#~ msgid "This will be prepended to the default keyword list." +#~ msgstr "Isto será inserido antes da lista de palavras-chave padrão." + +#~ msgid "This will be prepended to the default description." +#~ msgstr "Isto será inserido antes da descrição padrão." diff --git a/feincms/locale/pt/LC_MESSAGES/djangojs.po b/feincms/locale/pt/LC_MESSAGES/djangojs.po index 77851f198..f15fdb2b2 100644 --- a/feincms/locale/pt/LC_MESSAGES/djangojs.po +++ b/feincms/locale/pt/LC_MESSAGES/djangojs.po @@ -2,7 +2,7 @@ # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. -# +# msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" diff --git a/feincms/locale/pt_BR/LC_MESSAGES/django.po b/feincms/locale/pt_BR/LC_MESSAGES/django.po index d876a2baa..6b2e73258 100644 --- a/feincms/locale/pt_BR/LC_MESSAGES/django.po +++ b/feincms/locale/pt_BR/LC_MESSAGES/django.po @@ -1,744 +1,784 @@ # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. -# +# # Translators: # Guilherme Gondim , 2012 msgid "" msgstr "" "Project-Id-Version: FeinCMS\n" -"Report-Msgid-Bugs-To: http://github.com/feincms/feincms/issues\n" -"POT-Creation-Date: 2013-10-25 11:12+0200\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2014-07-20 14:59+0200\n" "PO-Revision-Date: 2013-10-25 09:15+0000\n" "Last-Translator: Matthias Kestenholz \n" -"Language-Team: Portuguese (Brazil) (http://www.transifex.com/projects/p/feincms/language/pt_BR/)\n" +"Language-Team: Portuguese (Brazil) (http://www.transifex.com/projects/p/" +"feincms/language/pt_BR/)\n" +"Language: pt_BR\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Language: pt_BR\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" -#: models.py:370 content/template/models.py:59 -msgid "template" -msgstr "modelo" - -#: models.py:510 -msgid "ordering" -msgstr "ordenação" - -#: translations.py:259 module/blog/extensions/translations.py:17 -#: module/extensions/translations.py:123 -msgid "language" -msgstr "idioma" - -#: admin/filterspecs.py:35 admin/filterspecs.py:70 +#: admin/filterspecs.py:47 admin/filterspecs.py:86 msgid "All" msgstr "Todos" -#: admin/filterspecs.py:46 module/page/models.py:172 +#: admin/filterspecs.py:58 module/page/models.py:178 msgid "Parent" msgstr "Ascendente" -#: admin/filterspecs.py:81 +#: admin/filterspecs.py:97 #: templates/admin/medialibrary/mediafile/change_list.html:14 msgid "Category" msgstr "Categoria" -#: admin/item_editor.py:159 +#: admin/item_editor.py:190 #, python-format msgid "Change %s" msgstr "Modificar %s" -#: admin/tree_editor.py:234 content/rss/models.py:20 -#: content/section/models.py:34 module/blog/models.py:29 -#: module/medialibrary/models.py:40 module/page/models.py:170 -#: module/page/models.py:226 +#: admin/tree_editor.py:294 content/rss/models.py:23 +#: content/section/models.py:35 module/blog/models.py:35 +#: module/medialibrary/models.py:45 module/page/models.py:172 +#: module/page/models.py:240 msgid "title" msgstr "título" -#: admin/tree_editor.py:407 +#: admin/tree_editor.py:353 admin/tree_editor.py:370 +#, fuzzy +#| msgid "You don't have the necessary permissions to edit this object" +msgid "You do not have permission to modify this object" +msgstr "Você não tem as permissões necessárias para editar este objeto" + +#: admin/tree_editor.py:491 +msgid "No permission" +msgstr "" + +#: admin/tree_editor.py:509 #, python-format msgid "%s has been moved to a new position." msgstr "%s foi movido para uma nova posição." -#: admin/tree_editor.py:411 +#: admin/tree_editor.py:512 msgid "Did not understand moving instruction." msgstr "A instrução para mover não foi entendida." -#: admin/tree_editor.py:420 +#: admin/tree_editor.py:523 msgid "actions" msgstr "ações" -#: admin/tree_editor.py:432 -#, python-format -msgid "Successfully deleted %s items." -msgstr "" +#: admin/tree_editor.py:552 +#, fuzzy, python-format +#| msgid "Successfully added %(count)d media file to %(category)s." +#| msgid_plural "Successfully added %(count)d media files to %(category)s." +msgid "Successfully deleted %(count)d items." +msgstr "Adicionado com sucesso %(count)d arquivo de mídia em %(category)s." -#: admin/tree_editor.py:440 +#: admin/tree_editor.py:565 #, python-format msgid "Delete selected %(verbose_name_plural)s" msgstr "" -#: content/application/models.py:218 +#: content/application/models.py:147 msgid "application content" msgstr "conteúdo de aplicação" -#: content/application/models.py:219 +#: content/application/models.py:148 msgid "application contents" msgstr "conteúdos de aplicação" -#: content/application/models.py:249 +#: content/application/models.py:179 msgid "application" msgstr "aplicação" -#: content/comments/models.py:24 +#: content/comments/models.py:32 msgid "enabled" msgstr "habilitado" -#: content/comments/models.py:24 +#: content/comments/models.py:33 msgid "New comments may be added" msgstr "Podem ser adicionados novos comentários" -#: content/comments/models.py:28 content/comments/models.py:29 +#: content/comments/models.py:37 content/comments/models.py:38 msgid "comments" msgstr "comentários" -#: content/comments/models.py:49 +#: content/comments/models.py:60 msgid "public" msgstr "público" -#: content/comments/models.py:49 +#: content/comments/models.py:61 msgid "not public" msgstr "não público" -#: content/contactform/models.py:18 +#: content/contactform/models.py:20 msgid "name" msgstr "nome" -#: content/contactform/models.py:19 +#: content/contactform/models.py:21 msgid "email" msgstr "e-mail" -#: content/contactform/models.py:20 +#: content/contactform/models.py:22 msgid "subject" msgstr "assunto" -#: content/contactform/models.py:23 content/raw/models.py:14 +#: content/contactform/models.py:26 content/raw/models.py:16 msgid "content" msgstr "conteúdo" -#: content/contactform/models.py:34 +#: content/contactform/models.py:37 msgid "contact form" msgstr "formulário de contato" -#: content/contactform/models.py:35 +#: content/contactform/models.py:38 msgid "contact forms" msgstr "formulários de contato" -#: content/file/models.py:20 content/file/models.py:25 -#: module/medialibrary/models.py:84 +#: content/file/models.py:23 content/file/models.py:28 +#: module/medialibrary/models.py:94 msgid "file" msgstr "arquivo" -#: content/file/models.py:26 +#: content/file/models.py:29 msgid "files" msgstr "arquivos" -#: content/image/models.py:45 content/image/models.py:54 +#: content/image/models.py:46 content/image/models.py:55 msgid "image" msgstr "imagem" -#: content/image/models.py:48 +#: content/image/models.py:49 msgid "alternate text" msgstr "" -#: content/image/models.py:49 +#: content/image/models.py:50 msgid "Description of image" msgstr "" -#: content/image/models.py:50 module/medialibrary/models.py:231 +#: content/image/models.py:51 module/medialibrary/models.py:260 msgid "caption" msgstr "legenda" -#: content/image/models.py:55 +#: content/image/models.py:56 msgid "images" msgstr "imagens" -#: content/image/models.py:80 +#: content/image/models.py:82 msgid "position" msgstr "posição" -#: content/image/models.py:88 +#: content/image/models.py:90 msgid "format" msgstr "" -#: content/medialibrary/models.py:49 content/section/models.py:36 -#: module/medialibrary/fields.py:59 module/medialibrary/models.py:97 +#: content/medialibrary/models.py:51 content/section/models.py:38 +#: module/medialibrary/fields.py:66 module/medialibrary/models.py:112 msgid "media file" msgstr "arquivo de mídia" -#: content/medialibrary/models.py:50 module/medialibrary/models.py:98 +#: content/medialibrary/models.py:52 module/medialibrary/models.py:113 msgid "media files" msgstr "arquivos de mídia" -#: content/medialibrary/models.py:58 content/section/models.py:52 -#: content/table/models.py:82 +#: content/medialibrary/models.py:64 content/section/models.py:59 msgid "type" msgstr "tipo" -#: content/raw/models.py:18 +#: content/raw/models.py:20 msgid "raw content" msgstr "conteúdo cru" -#: content/raw/models.py:19 +#: content/raw/models.py:21 msgid "raw contents" msgstr "conteúdos crus" -#: content/richtext/models.py:22 +#: content/richtext/models.py:24 msgid "HTML Tidy" msgstr "HTML Tidy" -#: content/richtext/models.py:23 +#: content/richtext/models.py:25 msgid "Ignore the HTML validation warnings" msgstr "Ignorar os avisos de validação HTML" -#: content/richtext/models.py:47 +#: content/richtext/models.py:55 #, python-format msgid "" "HTML validation produced %(count)d warnings. Please review the updated " "content below before continuing: %(messages)s" -msgstr "A validação HTML produziu %(count)d avisos. Por favor, revise o conteúdo atualizado abaixo antes de continuar: %(messages)s" +msgstr "" +"A validação HTML produziu %(count)d avisos. Por favor, revise o conteúdo " +"atualizado abaixo antes de continuar: %(messages)s" -#: content/richtext/models.py:84 content/section/models.py:35 +#: content/richtext/models.py:99 content/section/models.py:36 msgid "text" msgstr "texto" -#: content/richtext/models.py:88 +#: content/richtext/models.py:103 msgid "rich text" msgstr "texto rico" -#: content/richtext/models.py:89 +#: content/richtext/models.py:104 msgid "rich texts" msgstr "textos ricos" -#: content/rss/models.py:21 +#: content/rss/models.py:25 msgid "" "The rss field is updated several times a day. A change in the title will " "only be visible on the home page after the next feed update." -msgstr "O feed RSS é atualizado por várias vezes ao dia. Uma alteração do título só será visível na página após a próxima atualização do feed." +msgstr "" +"O feed RSS é atualizado por várias vezes ao dia. Uma alteração do título só " +"será visível na página após a próxima atualização do feed." -#: content/rss/models.py:22 +#: content/rss/models.py:28 msgid "link" msgstr "ligação" -#: content/rss/models.py:23 +#: content/rss/models.py:30 msgid "pre-rendered content" msgstr "conteúdo pré-renderizado" -#: content/rss/models.py:24 +#: content/rss/models.py:32 msgid "last updated" msgstr "última atualização" -#: content/rss/models.py:25 +#: content/rss/models.py:33 msgid "max. items" msgstr "número máximo" -#: content/rss/models.py:29 +#: content/rss/models.py:37 msgid "RSS feed" msgstr "feed RSS" -#: content/rss/models.py:30 +#: content/rss/models.py:38 msgid "RSS feeds" msgstr "feeds RSS" -#: content/section/models.py:41 +#: content/section/models.py:43 msgid "section" msgstr "seção" -#: content/section/models.py:42 +#: content/section/models.py:44 msgid "sections" msgstr "seções" -#: content/table/models.py:63 -msgid "plain" -msgstr "simples" - -#: content/table/models.py:64 -msgid "title row" -msgstr "linha de título" - -#: content/table/models.py:66 -msgid "title row and column" -msgstr "linha e coluna de título" - -#: content/table/models.py:72 -msgid "table" -msgstr "tabela" - -#: content/table/models.py:73 -msgid "tables" -msgstr "tabelas" - -#: content/table/models.py:87 -msgid "data" -msgstr "dados" - -#: content/template/models.py:51 +#: content/template/models.py:53 msgid "template content" msgstr "conteúdo de modelo" -#: content/template/models.py:52 +#: content/template/models.py:54 msgid "template contents" msgstr "conteúdos de modelo" -#: content/video/models.py:31 +#: content/template/models.py:63 models.py:400 +msgid "template" +msgstr "modelo" + +#: content/video/models.py:34 msgid "video link" msgstr "ligação de vídeo" -#: content/video/models.py:32 +#: content/video/models.py:36 msgid "" -"This should be a link to a youtube or vimeo video, i.e.: " -"http://www.youtube.com/watch?v=zmj1rpzDRZ0" -msgstr "Isto deve ser uma ligação para um vídeo do Youtube ou Vimeo, i.e.: http://www.youtube.com/watch?v=zmj1rpzDRZ0" +"This should be a link to a youtube or vimeo video, i.e.: http://www.youtube." +"com/watch?v=zmj1rpzDRZ0" +msgstr "" +"Isto deve ser uma ligação para um vídeo do Youtube ou Vimeo, i.e.: http://" +"www.youtube.com/watch?v=zmj1rpzDRZ0" -#: content/video/models.py:37 +#: content/video/models.py:41 msgid "video" msgstr "vídeo" -#: content/video/models.py:38 +#: content/video/models.py:42 msgid "videos" msgstr "vídeos" -#: contrib/tagging.py:117 +#: contrib/tagging.py:132 msgid "Tagging" msgstr "" -#: module/blog/models.py:28 +#: models.py:550 +msgid "ordering" +msgstr "ordenação" + +#: module/blog/extensions/tags.py:17 +msgid "tags" +msgstr "etiquetas" + +#: module/blog/extensions/translations.py:25 +#: module/extensions/translations.py:140 translations.py:282 +msgid "language" +msgstr "idioma" + +#: module/blog/extensions/translations.py:35 +#: module/extensions/translations.py:148 +msgid "translation of" +msgstr "tradução de" + +#: module/blog/extensions/translations.py:39 +#: module/extensions/translations.py:152 +msgid "Leave this empty for entries in the primary language." +msgstr "Deixe este campo em branco para entradas no idioma original." + +#: module/blog/extensions/translations.py:67 +#: templates/admin/feincms/item_editor.html:33 +msgid "available translations" +msgstr "traduções disponíveis" + +#: module/blog/models.py:33 msgid "published" msgstr "publicado" -#: module/blog/models.py:30 +#: module/blog/models.py:36 msgid "This is used for the generated navigation too." msgstr "Isto também é usado para a navegação gerada automaticamente." -#: module/blog/models.py:33 +#: module/blog/models.py:40 msgid "published on" msgstr "publicado em" -#: module/blog/models.py:34 -msgid "" -"Will be set automatically once you tick the `published` checkbox above." -msgstr "Será definido automaticamente assim que você ativar a caixa `publicado` acima." +#: module/blog/models.py:42 +msgid "Will be set automatically once you tick the `published` checkbox above." +msgstr "" +"Será definido automaticamente assim que você ativar a caixa `publicado` " +"acima." -#: module/blog/models.py:39 +#: module/blog/models.py:48 msgid "entry" msgstr "entrada" -#: module/blog/models.py:40 +#: module/blog/models.py:49 msgid "entries" msgstr "entradas" -#: module/blog/extensions/tags.py:12 -msgid "tags" -msgstr "etiquetas" - -#: module/blog/extensions/translations.py:20 -#: module/extensions/translations.py:126 -msgid "translation of" -msgstr "tradução de" - -#: module/blog/extensions/translations.py:23 -#: module/extensions/translations.py:129 -msgid "Leave this empty for entries in the primary language." -msgstr "Deixe este campo em branco para entradas no idioma original." - -#: module/blog/extensions/translations.py:44 -#: templates/admin/feincms/item_editor.html:43 -msgid "available translations" -msgstr "traduções disponíveis" - -#: module/extensions/changedate.py:32 +#: module/extensions/changedate.py:41 msgid "creation date" msgstr "data de criação" -#: module/extensions/changedate.py:33 +#: module/extensions/changedate.py:43 msgid "modification date" msgstr "data de modificação" -#: module/extensions/ct_tracker.py:140 +#: module/extensions/ct_tracker.py:156 msgid "content types" msgstr "tipos de conteúdo" -#: module/extensions/datepublisher.py:67 +#: module/extensions/datepublisher.py:86 msgid "publication date" msgstr "Otimização para motores de busca" -#: module/extensions/datepublisher.py:70 +#: module/extensions/datepublisher.py:90 msgid "publication end date" msgstr "publicar até" -#: module/extensions/datepublisher.py:72 +#: module/extensions/datepublisher.py:93 msgid "Leave empty if the entry should stay active forever." msgstr "Deixe em branco se a entrada deve ficar ativa para sempre." -#: module/extensions/datepublisher.py:103 +#: module/extensions/datepublisher.py:128 msgid "visible from - to" msgstr "visível de - até" -#: module/extensions/datepublisher.py:113 +#: module/extensions/datepublisher.py:139 msgid "Date-based publishing" msgstr "Publicação baseada em datas" -#: module/extensions/featured.py:9 +#: module/extensions/featured.py:15 msgid "featured" msgstr "recomendado" -#: module/extensions/featured.py:14 +#: module/extensions/featured.py:21 msgid "Featured" msgstr "Recomendado" -#: module/extensions/seo.py:9 +#: module/extensions/seo.py:16 msgid "meta keywords" msgstr "meta palavras-chave" -#: module/extensions/seo.py:10 -msgid "This will be prepended to the default keyword list." -msgstr "Isto será inserido antes da lista de palavras-chave padrão." +#: module/extensions/seo.py:18 +msgid "Keywords are ignored by most search engines." +msgstr "" -#: module/extensions/seo.py:11 +#: module/extensions/seo.py:20 msgid "meta description" msgstr "meta descrição" -#: module/extensions/seo.py:12 -msgid "This will be prepended to the default description." -msgstr "Isto será inserido antes da descrição padrão." +#: module/extensions/seo.py:22 +msgid "" +"This text is displayed on the search results page. It is however not used " +"for the SEO ranking. Text longer than 140 characters is truncated." +msgstr "" -#: module/extensions/seo.py:17 +#: module/extensions/seo.py:32 msgid "Search engine optimization" msgstr "Otimização para motores de busca" -#: module/extensions/translations.py:207 +#: module/extensions/translations.py:249 msgid "Edit translation" msgstr "Editar a tradução" -#: module/extensions/translations.py:210 +#: module/extensions/translations.py:256 msgid "Create translation" msgstr "Criar tradução" -#: module/extensions/translations.py:215 +#: module/extensions/translations.py:264 msgid "translations" msgstr "traduções" -#: module/medialibrary/forms.py:26 +#: module/medialibrary/forms.py:31 msgid "This would create a loop in the hierarchy" msgstr "" -#: module/medialibrary/forms.py:64 +#: module/medialibrary/forms.py:77 #, python-format msgid "" "Cannot overwrite with different file type (attempt to overwrite a " "%(old_ext)s with a %(new_ext)s)" msgstr "" -#: module/medialibrary/modeladmins.py:58 +#: module/medialibrary/modeladmins.py:63 #, python-format msgid "Successfully added %(count)d media file to %(category)s." msgid_plural "Successfully added %(count)d media files to %(category)s." msgstr[0] "Adicionado com sucesso %(count)d arquivo de mídia em %(category)s." msgstr[1] "Adicionado com sucesso %(count)d arquivos de mídia em %(category)s." -#: module/medialibrary/modeladmins.py:77 +#: module/medialibrary/modeladmins.py:84 msgid "Add selected media files to category" msgstr "Adicionar arquivos de mídia selecionados à categoria" -#: module/medialibrary/modeladmins.py:86 +#: module/medialibrary/modeladmins.py:94 #, python-format msgid "ZIP file exported as %s" msgstr "" -#: module/medialibrary/modeladmins.py:88 +#: module/medialibrary/modeladmins.py:96 #, python-format msgid "ZIP file export failed: %s" msgstr "" -#: module/medialibrary/modeladmins.py:93 +#: module/medialibrary/modeladmins.py:104 msgid "Export selected media files as zip file" msgstr "" -#: module/medialibrary/modeladmins.py:136 +#: module/medialibrary/modeladmins.py:157 #: templates/admin/feincms/page/page/item_editor.html:15 msgid "Preview" msgstr "Pré-visualização" -#: module/medialibrary/modeladmins.py:141 module/medialibrary/models.py:88 +#: module/medialibrary/modeladmins.py:162 module/medialibrary/models.py:103 msgid "file size" msgstr "tamanho do arquivo" -#: module/medialibrary/modeladmins.py:146 module/medialibrary/models.py:86 +#: module/medialibrary/modeladmins.py:167 module/medialibrary/models.py:100 msgid "created" msgstr "criado em" -#: module/medialibrary/modeladmins.py:165 module/medialibrary/models.py:85 +#: module/medialibrary/modeladmins.py:187 module/medialibrary/models.py:97 msgid "file type" msgstr "tipo de arquivo" -#: module/medialibrary/modeladmins.py:186 +#: module/medialibrary/modeladmins.py:211 msgid "file info" msgstr "informação do arquivo" -#: module/medialibrary/modeladmins.py:198 +#: module/medialibrary/modeladmins.py:224 #, python-format msgid "%d files imported" msgstr "%d arquivos importados" -#: module/medialibrary/modeladmins.py:200 +#: module/medialibrary/modeladmins.py:226 #, python-format msgid "ZIP import failed: %s" msgstr "" -#: module/medialibrary/modeladmins.py:202 +#: module/medialibrary/modeladmins.py:228 msgid "No input file given" msgstr "Nenhum arquivo de entrada fornecido" -#: module/medialibrary/models.py:43 +#: module/medialibrary/models.py:49 msgid "parent" msgstr "ascendente" -#: module/medialibrary/models.py:45 module/page/models.py:171 +#: module/medialibrary/models.py:51 module/page/models.py:175 msgid "slug" msgstr "slug" -#: module/medialibrary/models.py:49 +#: module/medialibrary/models.py:55 msgid "category" msgstr "categoria" -#: module/medialibrary/models.py:50 module/medialibrary/models.py:90 +#: module/medialibrary/models.py:56 module/medialibrary/models.py:106 msgid "categories" msgstr "categorias" -#: module/medialibrary/models.py:87 +#: module/medialibrary/models.py:101 msgid "copyright" msgstr "copyright" -#: module/medialibrary/models.py:202 +#: module/medialibrary/models.py:219 msgid "Image" msgstr "Imagem" -#: module/medialibrary/models.py:203 +#: module/medialibrary/models.py:221 msgid "Video" msgstr "Vídeo" -#: module/medialibrary/models.py:204 +#: module/medialibrary/models.py:224 msgid "Audio" msgstr "Áudio" -#: module/medialibrary/models.py:205 +#: module/medialibrary/models.py:226 msgid "PDF document" msgstr "documento PDF" -#: module/medialibrary/models.py:206 +#: module/medialibrary/models.py:227 msgid "Flash" msgstr "Flash" -#: module/medialibrary/models.py:207 +#: module/medialibrary/models.py:228 msgid "Text" msgstr "Texto" -#: module/medialibrary/models.py:208 +#: module/medialibrary/models.py:229 msgid "Rich Text" msgstr "Texto Rico" -#: module/medialibrary/models.py:209 +#: module/medialibrary/models.py:230 msgid "Zip archive" msgstr "Arquivo ZIP" -#: module/medialibrary/models.py:210 +#: module/medialibrary/models.py:231 msgid "Microsoft Word" msgstr "Microsoft Word" -#: module/medialibrary/models.py:211 +#: module/medialibrary/models.py:233 msgid "Microsoft Excel" msgstr "Microsoft Excel" -#: module/medialibrary/models.py:212 +#: module/medialibrary/models.py:235 msgid "Microsoft PowerPoint" msgstr "Microsoft PowerPoint" -#: module/medialibrary/models.py:213 +#: module/medialibrary/models.py:237 msgid "Binary" msgstr "Binário" -#: module/medialibrary/models.py:232 +#: module/medialibrary/models.py:261 msgid "description" msgstr "descrição" -#: module/medialibrary/models.py:235 +#: module/medialibrary/models.py:264 msgid "media file translation" msgstr "tradução do arquivo de mídia" -#: module/medialibrary/models.py:236 +#: module/medialibrary/models.py:265 msgid "media file translations" msgstr "traduções do arquivo de mídia" -#: module/page/forms.py:172 +#: module/page/extensions/excerpt.py:18 +msgid "excerpt" +msgstr "trecho" + +#: module/page/extensions/excerpt.py:21 +msgid "Add a brief excerpt summarizing the content of this page." +msgstr "Adicione um breve trecho que sumarize o conteúdo desta página." + +#: module/page/extensions/excerpt.py:25 +msgid "Excerpt" +msgstr "Trecho" + +#: module/page/extensions/navigation.py:86 +#: module/page/extensions/navigation.py:115 +msgid "navigation extension" +msgstr "extensão de navegação" + +#: module/page/extensions/navigation.py:119 +msgid "" +"Select the module providing subpages for this page if you need to customize " +"the navigation." +msgstr "" +"Selecione o módulo que fornece sub-páginas para esta página se você precisa " +"personalizar a navegação." + +#: module/page/extensions/navigation.py:134 +msgid "Navigation extension" +msgstr "Extensão de navegação" + +#: module/page/extensions/navigationgroups.py:20 +msgid "Default" +msgstr "" + +#: module/page/extensions/navigationgroups.py:21 +msgid "Footer" +msgstr "" + +#: module/page/extensions/navigationgroups.py:28 +#, fuzzy +#| msgid "in navigation" +msgid "navigation group" +msgstr "na navegação" + +#: module/page/extensions/relatedpages.py:21 +msgid "Select pages that should be listed as related content." +msgstr "Selecione as páginas que devam ser listadas como conteúdo relacionado." + +#: module/page/extensions/relatedpages.py:26 +msgid "Related pages" +msgstr "Páginas relacionadas" + +#: module/page/extensions/sites.py:21 +msgid "Site" +msgstr "Site" + +#: module/page/extensions/symlinks.py:22 +msgid "symlinked page" +msgstr "página de ligação" + +#: module/page/extensions/symlinks.py:23 +msgid "All content is inherited from this page if given." +msgstr "O conteúdo é herdado desta página, se fornecida." + +#: module/page/extensions/titles.py:19 +msgid "content title" +msgstr "título do conteúdo" + +#: module/page/extensions/titles.py:22 +msgid "The first line is the main title, the following lines are subtitles." +msgstr "" +"A primeira linha é o título principal, as linhas subsequentes são sub-" +"títulos." + +#: module/page/extensions/titles.py:26 +msgid "page title" +msgstr "título da página" + +#: module/page/extensions/titles.py:30 +#, fuzzy +#| msgid "Page title for browser window. Same as title by default." +msgid "" +"Page title for browser window. Same as title bydefault. Must not be longer " +"than 70 characters." +msgstr "" +"Título da página para a janela do navegador. Por padrão, o mesmo que o " +"título." + +#: module/page/extensions/titles.py:60 +msgid "Titles" +msgstr "Títulos" + +#: module/page/forms.py:187 msgid "This URL is already taken by an active page." msgstr "Esta URL já está ocupada por uma página ativa." -#: module/page/forms.py:190 +#: module/page/forms.py:206 msgid "This URL is already taken by another active page." msgstr "Esta URL já está ocupada por outra página ativa." -#: module/page/modeladmins.py:41 +#: module/page/forms.py:211 +msgid "This page does not allow attachment of child pages" +msgstr "" + +#: module/page/modeladmins.py:42 msgid "Other options" msgstr "Outras opções" -#: module/page/modeladmins.py:90 module/page/models.py:174 +#: module/page/modeladmins.py:80 module/page/models.py:182 msgid "in navigation" msgstr "na navegação" -#: module/page/modeladmins.py:105 +#: module/page/modeladmins.py:109 module/page/modeladmins.py:111 msgid "Add child page" msgstr "Adicionar página descendente" -#: module/page/modeladmins.py:107 +#: module/page/modeladmins.py:120 module/page/modeladmins.py:122 #: templates/admin/feincms/content_inline.html:9 msgid "View on site" msgstr "Ver no site" -#: module/page/modeladmins.py:126 +#: module/page/modeladmins.py:142 #, python-format -msgid "Add %(language)s Translation of \"%(page)s\"" +msgid "Add %(language)s translation of \"%(page)s\"" msgstr "" -#: module/page/modeladmins.py:156 +#: module/page/modeladmins.py:177 msgid "" "The content from the original translation has been copied to the newly " "created page." msgstr "" -#: module/page/modeladmins.py:170 +#: module/page/modeladmins.py:197 msgid "You don't have the necessary permissions to edit this object" msgstr "Você não tem as permissões necessárias para editar este objeto" -#: module/page/modeladmins.py:185 +#: module/page/modeladmins.py:223 msgid "inherited" msgstr "herdado" -#: module/page/modeladmins.py:189 +#: module/page/modeladmins.py:229 msgid "extensions" msgstr "extensões" -#: module/page/modeladmins.py:193 module/page/models.py:206 +#: module/page/modeladmins.py:233 module/page/models.py:221 msgid "is active" msgstr "ativo" -#: module/page/models.py:167 +#: module/page/models.py:169 msgid "active" msgstr "ativo" -#: module/page/models.py:175 +#: module/page/models.py:173 +#, fuzzy +#| msgid "This is used for the generated navigation too." +msgid "This title is also used for navigation menu items." +msgstr "Isto também é usado para a navegação gerada automaticamente." + +#: module/page/models.py:176 +msgid "This is used to build the URL for this page" +msgstr "" + +#: module/page/models.py:184 msgid "override URL" msgstr "URL efetiva" -#: module/page/models.py:176 +#: module/page/models.py:186 msgid "" "Override the target URL. Be sure to include slashes at the beginning and at " -"the end if it is a local URL. This affects both the navigation and subpages'" -" URLs." -msgstr "Substitui a URL de destino. Certifique-se de incluir barras no início e no final, se for uma URL local. Isso afeta tanto a navegação e URLs de subpáginas." +"the end if it is a local URL. This affects both the navigation and subpages' " +"URLs." +msgstr "" +"Substitui a URL de destino. Certifique-se de incluir barras no início e no " +"final, se for uma URL local. Isso afeta tanto a navegação e URLs de " +"subpáginas." -#: module/page/models.py:177 +#: module/page/models.py:190 msgid "redirect to" msgstr "redirecionar para" -#: module/page/models.py:178 +#: module/page/models.py:193 msgid "Target URL for automatic redirects or the primary key of a page." msgstr "" -#: module/page/models.py:180 +#: module/page/models.py:196 msgid "Cached URL" msgstr "URL em cache" -#: module/page/models.py:395 +#: module/page/models.py:298 +#, python-format +msgid "" +"This %(page_class)s uses a singleton template, and " +"FEINCMS_SINGLETON_TEMPLATE_DELETION_ALLOWED=False" +msgstr "" + +#: module/page/models.py:426 msgid "page" msgstr "página" -#: module/page/models.py:396 +#: module/page/models.py:427 msgid "pages" msgstr "páginas" -#: module/page/extensions/excerpt.py:9 -msgid "excerpt" -msgstr "trecho" - -#: module/page/extensions/excerpt.py:10 -msgid "Add a brief excerpt summarizing the content of this page." -msgstr "Adicione um breve trecho que sumarize o conteúdo desta página." - -#: module/page/extensions/excerpt.py:12 -msgid "Excerpt" -msgstr "Trecho" - -#: module/page/extensions/navigation.py:82 -#: module/page/extensions/navigation.py:107 -msgid "navigation extension" -msgstr "extensão de navegação" - -#: module/page/extensions/navigation.py:110 -msgid "" -"Select the module providing subpages for this page if you need to customize " -"the navigation." -msgstr "Selecione o módulo que fornece sub-páginas para esta página se você precisa personalizar a navegação." - -#: module/page/extensions/navigation.py:125 -msgid "Navigation extension" -msgstr "Extensão de navegação" - -#: module/page/extensions/relatedpages.py:14 -msgid "Select pages that should be listed as related content." -msgstr "Selecione as páginas que devam ser listadas como conteúdo relacionado." - -#: module/page/extensions/relatedpages.py:19 -msgid "Related pages" -msgstr "Páginas relacionadas" - -#: module/page/extensions/sites.py:16 -msgid "Site" -msgstr "Site" - -#: module/page/extensions/symlinks.py:15 -msgid "symlinked page" -msgstr "página de ligação" - -#: module/page/extensions/symlinks.py:16 -msgid "All content is inherited from this page if given." -msgstr "O conteúdo é herdado desta página, se fornecida." - -#: module/page/extensions/titles.py:13 -msgid "content title" -msgstr "título do conteúdo" - -#: module/page/extensions/titles.py:14 -msgid "The first line is the main title, the following lines are subtitles." -msgstr "A primeira linha é o título principal, as linhas subsequentes são sub-títulos." - -#: module/page/extensions/titles.py:15 -msgid "page title" -msgstr "título da página" - -#: module/page/extensions/titles.py:16 -msgid "Page title for browser window. Same as title by default." -msgstr "Título da página para a janela do navegador. Por padrão, o mesmo que o título." - -#: module/page/extensions/titles.py:43 -msgid "Titles" -msgstr "Títulos" - -#: templates/admin/filter.html:3 -#, python-format -msgid " By %(filter_title)s " -msgstr " Por %(filter_title)s " - #: templates/admin/feincms/_messages_js.html:4 msgid "Really delete item?" msgstr "Confirma a remoção do item?" @@ -765,14 +805,14 @@ msgstr "Alterar o modelo" #: templates/admin/feincms/_messages_js.html:8 msgid "Really change template?
    All changes are saved." -msgstr "Confirma a alteração do modelo?
    Todas as alterações serão salvas." +msgstr "" +"Confirma a alteração do modelo?
    Todas as alterações serão salvas." #: templates/admin/feincms/_messages_js.html:9 #, python-format msgid "" -"Really change template?
    All changes are saved and content from " -"%%(source_regions)s is moved to " -"%%(target_region)s." +"Really change template?
    All changes are saved and content from " +"%%(source_regions)s is moved to %%(target_region)s." msgstr "" #: templates/admin/feincms/_messages_js.html:12 @@ -807,22 +847,24 @@ msgstr "Região vazia" msgid "" "Content from the parent site is automatically inherited. To override this " "behaviour, add some content." -msgstr "O conteúdo é herdado automaticamente da página ascendente. Para mudar este comportamento, adicione algum conteúdo." +msgstr "" +"O conteúdo é herdado automaticamente da página ascendente. Para mudar este " +"comportamento, adicione algum conteúdo." #: templates/admin/feincms/content_editor.html:33 msgid "Add new item" msgstr "Adicionar novo item" -#: templates/admin/feincms/content_inline.html:91 +#: templates/admin/feincms/content_inline.html:93 #, python-format msgid "Add another %(verbose_name)s" msgstr "Adicionar mais um %(verbose_name)s" -#: templates/admin/feincms/content_inline.html:94 +#: templates/admin/feincms/content_inline.html:96 msgid "Remove" msgstr "Remover" -#: templates/admin/feincms/fe_editor.html:40 +#: templates/admin/feincms/fe_editor.html:30 msgid "Save" msgstr "Guardar" @@ -850,13 +892,21 @@ msgstr "para baixo" msgid "remove" msgstr "remover" -#: templates/admin/feincms/recover_form.html:8 -#: templates/admin/feincms/revision_form.html:8 +#: templates/admin/feincms/page/page/item_editor.html:10 +msgid "Edit on site" +msgstr "Editar no site" + #: templates/admin/feincms/page/page/item_editor.html:23 #: templates/admin/feincms/page/page/tree_editor.html:7 +#: templates/admin/feincms/recover_form.html:8 +#: templates/admin/feincms/revision_form.html:8 msgid "Home" msgstr "Página inicial" +#: templates/admin/feincms/page/page/item_editor.html:27 +msgid "Add" +msgstr "Adicionar" + #: templates/admin/feincms/recover_form.html:11 #, python-format msgid "Recover deleted %(verbose_name)s" @@ -864,7 +914,8 @@ msgstr "Recuperar %(verbose_name)s removido" #: templates/admin/feincms/recover_form.html:17 msgid "Press the save button below to recover this version of the object." -msgstr "Pressione o botão de salvar abaixo para recuperar esta versão do objeto." +msgstr "" +"Pressione o botão de salvar abaixo para recuperar esta versão do objeto." #: templates/admin/feincms/revision_form.html:12 msgid "History" @@ -877,31 +928,29 @@ msgstr "Reverter %(verbose_name)s" #: templates/admin/feincms/revision_form.html:24 msgid "Press the save button below to revert to this version of the object." -msgstr "Pressione o botão de salvar abaixo para reverter para esta versão do objeto." +msgstr "" +"Pressione o botão de salvar abaixo para reverter para esta versão do objeto." -#: templates/admin/feincms/tree_editor.html:32 +#: templates/admin/feincms/tree_editor.html:22 msgid "Shortcuts" msgstr "Atalhos" -#: templates/admin/feincms/tree_editor.html:34 +#: templates/admin/feincms/tree_editor.html:24 msgid "Collapse tree" msgstr "Contrair árvore" -#: templates/admin/feincms/tree_editor.html:35 +#: templates/admin/feincms/tree_editor.html:25 msgid "Expand tree" msgstr "Expandir árvore" -#: templates/admin/feincms/tree_editor.html:38 +#: templates/admin/feincms/tree_editor.html:29 msgid "Filter" msgstr "Filtrar" -#: templates/admin/feincms/page/page/item_editor.html:10 -msgid "Edit on site" -msgstr "Editar no site" - -#: templates/admin/feincms/page/page/item_editor.html:27 -msgid "Add" -msgstr "Adicionar" +#: templates/admin/filter.html:3 +#, python-format +msgid " By %(filter_title)s " +msgstr " Por %(filter_title)s " #: templates/admin/medialibrary/add_to_category.html:5 #: templates/admin/medialibrary/add_to_category.html:9 @@ -914,7 +963,8 @@ msgstr "Selecione a categoria a aplicar:" #: templates/admin/medialibrary/add_to_category.html:17 msgid "The following media files will be added to the selected category:" -msgstr "Os seguintes arquivos de mídia serão adicionados à categoria selecionada:" +msgstr "" +"Os seguintes arquivos de mídia serão adicionados à categoria selecionada:" #: templates/admin/medialibrary/add_to_category.html:22 msgid "Add to category" @@ -945,9 +995,14 @@ msgstr "%(comment_count)s comentários." #, python-format msgid "" "\n" -" %(comment_username)s said on %(comment_submit_date)s
    \n" +" %(comment_username)s said on %(comment_submit_date)s
    \n" +" " +msgstr "" +"\n" +" %(comment_username)s disse em " +"%(comment_submit_date)s
    \n" " " -msgstr "\n %(comment_username)s disse em %(comment_submit_date)s
    \n " #: templates/content/comments/default.html:28 msgid "No comments." @@ -964,3 +1019,27 @@ msgstr "Enviar" #: templates/content/contactform/thanks.html:3 msgid "Thanks!" msgstr "Obrigado!" + +#~ msgid "plain" +#~ msgstr "simples" + +#~ msgid "title row" +#~ msgstr "linha de título" + +#~ msgid "title row and column" +#~ msgstr "linha e coluna de título" + +#~ msgid "table" +#~ msgstr "tabela" + +#~ msgid "tables" +#~ msgstr "tabelas" + +#~ msgid "data" +#~ msgstr "dados" + +#~ msgid "This will be prepended to the default keyword list." +#~ msgstr "Isto será inserido antes da lista de palavras-chave padrão." + +#~ msgid "This will be prepended to the default description." +#~ msgstr "Isto será inserido antes da descrição padrão." diff --git a/feincms/locale/ro/LC_MESSAGES/django.po b/feincms/locale/ro/LC_MESSAGES/django.po index 60c3517bd..594d17e0e 100644 --- a/feincms/locale/ro/LC_MESSAGES/django.po +++ b/feincms/locale/ro/LC_MESSAGES/django.po @@ -1,446 +1,438 @@ # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. -# +# # Translators: # Gabriel Kovacs , 2010 msgid "" msgstr "" "Project-Id-Version: FeinCMS\n" -"Report-Msgid-Bugs-To: http://github.com/feincms/feincms/issues\n" -"POT-Creation-Date: 2013-10-25 11:12+0200\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2014-07-20 14:59+0200\n" "PO-Revision-Date: 2013-10-25 09:15+0000\n" "Last-Translator: Matthias Kestenholz \n" -"Language-Team: Romanian (http://www.transifex.com/projects/p/feincms/language/ro/)\n" +"Language-Team: Romanian (http://www.transifex.com/projects/p/feincms/" +"language/ro/)\n" +"Language: ro\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Language: ro\n" -"Plural-Forms: nplurals=3; plural=(n==1?0:(((n%100>19)||((n%100==0)&&(n!=0)))?2:1));\n" - -#: models.py:370 content/template/models.py:59 -msgid "template" -msgstr "șablon" - -#: models.py:510 -msgid "ordering" -msgstr "ordonare" +"Plural-Forms: nplurals=3; plural=(n==1?0:(((n%100>19)||((n%100==0)&&(n!=0)))?" +"2:1));\n" -#: translations.py:259 module/blog/extensions/translations.py:17 -#: module/extensions/translations.py:123 -msgid "language" -msgstr "limbă" - -#: admin/filterspecs.py:35 admin/filterspecs.py:70 +#: admin/filterspecs.py:47 admin/filterspecs.py:86 msgid "All" msgstr "" -#: admin/filterspecs.py:46 module/page/models.py:172 +#: admin/filterspecs.py:58 module/page/models.py:178 msgid "Parent" msgstr "" -#: admin/filterspecs.py:81 +#: admin/filterspecs.py:97 #: templates/admin/medialibrary/mediafile/change_list.html:14 msgid "Category" msgstr "" -#: admin/item_editor.py:159 +#: admin/item_editor.py:190 #, python-format msgid "Change %s" msgstr "Modifică %s" -#: admin/tree_editor.py:234 content/rss/models.py:20 -#: content/section/models.py:34 module/blog/models.py:29 -#: module/medialibrary/models.py:40 module/page/models.py:170 -#: module/page/models.py:226 +#: admin/tree_editor.py:294 content/rss/models.py:23 +#: content/section/models.py:35 module/blog/models.py:35 +#: module/medialibrary/models.py:45 module/page/models.py:172 +#: module/page/models.py:240 msgid "title" msgstr "titlu" -#: admin/tree_editor.py:407 +#: admin/tree_editor.py:353 admin/tree_editor.py:370 +msgid "You do not have permission to modify this object" +msgstr "" + +#: admin/tree_editor.py:491 +msgid "No permission" +msgstr "" + +#: admin/tree_editor.py:509 #, python-format msgid "%s has been moved to a new position." msgstr "" -#: admin/tree_editor.py:411 +#: admin/tree_editor.py:512 msgid "Did not understand moving instruction." msgstr "" -#: admin/tree_editor.py:420 +#: admin/tree_editor.py:523 msgid "actions" msgstr "acțiuni" -#: admin/tree_editor.py:432 +#: admin/tree_editor.py:552 #, python-format -msgid "Successfully deleted %s items." +msgid "Successfully deleted %(count)d items." msgstr "" -#: admin/tree_editor.py:440 +#: admin/tree_editor.py:565 #, python-format msgid "Delete selected %(verbose_name_plural)s" msgstr "" -#: content/application/models.py:218 +#: content/application/models.py:147 msgid "application content" msgstr "conținutul aplicației" -#: content/application/models.py:219 +#: content/application/models.py:148 msgid "application contents" msgstr "conținuturile aplicației" -#: content/application/models.py:249 +#: content/application/models.py:179 msgid "application" msgstr "aplicație" -#: content/comments/models.py:24 +#: content/comments/models.py:32 msgid "enabled" msgstr "" -#: content/comments/models.py:24 +#: content/comments/models.py:33 msgid "New comments may be added" msgstr "" -#: content/comments/models.py:28 content/comments/models.py:29 +#: content/comments/models.py:37 content/comments/models.py:38 msgid "comments" msgstr "" -#: content/comments/models.py:49 +#: content/comments/models.py:60 msgid "public" msgstr "" -#: content/comments/models.py:49 +#: content/comments/models.py:61 msgid "not public" msgstr "" -#: content/contactform/models.py:18 +#: content/contactform/models.py:20 msgid "name" msgstr "nume" -#: content/contactform/models.py:19 +#: content/contactform/models.py:21 msgid "email" msgstr "e-mail" -#: content/contactform/models.py:20 +#: content/contactform/models.py:22 msgid "subject" msgstr "subiect" -#: content/contactform/models.py:23 content/raw/models.py:14 +#: content/contactform/models.py:26 content/raw/models.py:16 msgid "content" msgstr "conținut" -#: content/contactform/models.py:34 +#: content/contactform/models.py:37 msgid "contact form" msgstr "formular de contact" -#: content/contactform/models.py:35 +#: content/contactform/models.py:38 msgid "contact forms" msgstr "formulare de contact" -#: content/file/models.py:20 content/file/models.py:25 -#: module/medialibrary/models.py:84 +#: content/file/models.py:23 content/file/models.py:28 +#: module/medialibrary/models.py:94 msgid "file" msgstr "fișier" -#: content/file/models.py:26 +#: content/file/models.py:29 msgid "files" msgstr "fișiere" -#: content/image/models.py:45 content/image/models.py:54 +#: content/image/models.py:46 content/image/models.py:55 msgid "image" msgstr "imagine" -#: content/image/models.py:48 +#: content/image/models.py:49 msgid "alternate text" msgstr "" -#: content/image/models.py:49 +#: content/image/models.py:50 msgid "Description of image" msgstr "" -#: content/image/models.py:50 module/medialibrary/models.py:231 +#: content/image/models.py:51 module/medialibrary/models.py:260 msgid "caption" msgstr "legendă" -#: content/image/models.py:55 +#: content/image/models.py:56 msgid "images" msgstr "imagini" -#: content/image/models.py:80 +#: content/image/models.py:82 msgid "position" msgstr "poziție" -#: content/image/models.py:88 +#: content/image/models.py:90 msgid "format" msgstr "" -#: content/medialibrary/models.py:49 content/section/models.py:36 -#: module/medialibrary/fields.py:59 module/medialibrary/models.py:97 +#: content/medialibrary/models.py:51 content/section/models.py:38 +#: module/medialibrary/fields.py:66 module/medialibrary/models.py:112 msgid "media file" msgstr "fișier media" -#: content/medialibrary/models.py:50 module/medialibrary/models.py:98 +#: content/medialibrary/models.py:52 module/medialibrary/models.py:113 msgid "media files" msgstr "fișiere media" -#: content/medialibrary/models.py:58 content/section/models.py:52 -#: content/table/models.py:82 +#: content/medialibrary/models.py:64 content/section/models.py:59 msgid "type" msgstr "tip" -#: content/raw/models.py:18 +#: content/raw/models.py:20 msgid "raw content" msgstr "conținut neformatat" -#: content/raw/models.py:19 +#: content/raw/models.py:21 msgid "raw contents" msgstr "conținuturi neformatate" -#: content/richtext/models.py:22 +#: content/richtext/models.py:24 msgid "HTML Tidy" msgstr "" -#: content/richtext/models.py:23 +#: content/richtext/models.py:25 msgid "Ignore the HTML validation warnings" msgstr "" -#: content/richtext/models.py:47 +#: content/richtext/models.py:55 #, python-format msgid "" "HTML validation produced %(count)d warnings. Please review the updated " "content below before continuing: %(messages)s" msgstr "" -#: content/richtext/models.py:84 content/section/models.py:35 +#: content/richtext/models.py:99 content/section/models.py:36 msgid "text" msgstr "text" -#: content/richtext/models.py:88 +#: content/richtext/models.py:103 msgid "rich text" msgstr "text formatabil" -#: content/richtext/models.py:89 +#: content/richtext/models.py:104 msgid "rich texts" msgstr "texte formatabile" -#: content/rss/models.py:21 +#: content/rss/models.py:25 msgid "" "The rss field is updated several times a day. A change in the title will " "only be visible on the home page after the next feed update." -msgstr "Fluxul RSS este actualizat de mai multe ori pe zi. O modificare a titlului va fi vizibilă in prima pagină după următoarea actualizare a fluxului." +msgstr "" +"Fluxul RSS este actualizat de mai multe ori pe zi. O modificare a titlului " +"va fi vizibilă in prima pagină după următoarea actualizare a fluxului." -#: content/rss/models.py:22 +#: content/rss/models.py:28 msgid "link" msgstr "legătură" -#: content/rss/models.py:23 +#: content/rss/models.py:30 msgid "pre-rendered content" msgstr "conținut predefinit" -#: content/rss/models.py:24 +#: content/rss/models.py:32 msgid "last updated" msgstr "ultima actualizare" -#: content/rss/models.py:25 +#: content/rss/models.py:33 msgid "max. items" msgstr "nr. de obiecte maxime" -#: content/rss/models.py:29 +#: content/rss/models.py:37 msgid "RSS feed" msgstr "flux RSS" -#: content/rss/models.py:30 +#: content/rss/models.py:38 msgid "RSS feeds" msgstr "fluxuri RSS" -#: content/section/models.py:41 +#: content/section/models.py:43 msgid "section" msgstr "" -#: content/section/models.py:42 +#: content/section/models.py:44 msgid "sections" msgstr "" -#: content/table/models.py:63 -msgid "plain" -msgstr "simplu" - -#: content/table/models.py:64 -msgid "title row" -msgstr "titlu" - -#: content/table/models.py:66 -msgid "title row and column" -msgstr "titlu rând și coloană" - -#: content/table/models.py:72 -msgid "table" -msgstr "tabel" - -#: content/table/models.py:73 -msgid "tables" -msgstr "tabele" - -#: content/table/models.py:87 -msgid "data" -msgstr "data" - -#: content/template/models.py:51 +#: content/template/models.py:53 msgid "template content" msgstr "" -#: content/template/models.py:52 +#: content/template/models.py:54 msgid "template contents" msgstr "" -#: content/video/models.py:31 +#: content/template/models.py:63 models.py:400 +msgid "template" +msgstr "șablon" + +#: content/video/models.py:34 msgid "video link" msgstr "legătură film" -#: content/video/models.py:32 +#: content/video/models.py:36 msgid "" -"This should be a link to a youtube or vimeo video, i.e.: " -"http://www.youtube.com/watch?v=zmj1rpzDRZ0" -msgstr "Aceasta ar trebui să fie o legătură către un film de pe youtube sau vimeo, de ex.: http://www.youtube.com/watch?v=zmj1rpzDRZ0" +"This should be a link to a youtube or vimeo video, i.e.: http://www.youtube." +"com/watch?v=zmj1rpzDRZ0" +msgstr "" +"Aceasta ar trebui să fie o legătură către un film de pe youtube sau vimeo, " +"de ex.: http://www.youtube.com/watch?v=zmj1rpzDRZ0" -#: content/video/models.py:37 +#: content/video/models.py:41 msgid "video" msgstr "film" -#: content/video/models.py:38 +#: content/video/models.py:42 msgid "videos" msgstr "filme" -#: contrib/tagging.py:117 +#: contrib/tagging.py:132 msgid "Tagging" msgstr "" -#: module/blog/models.py:28 +#: models.py:550 +msgid "ordering" +msgstr "ordonare" + +#: module/blog/extensions/tags.py:17 +msgid "tags" +msgstr "etichete" + +#: module/blog/extensions/translations.py:25 +#: module/extensions/translations.py:140 translations.py:282 +msgid "language" +msgstr "limbă" + +#: module/blog/extensions/translations.py:35 +#: module/extensions/translations.py:148 +msgid "translation of" +msgstr "tradus de" + +#: module/blog/extensions/translations.py:39 +#: module/extensions/translations.py:152 +msgid "Leave this empty for entries in the primary language." +msgstr "" + +#: module/blog/extensions/translations.py:67 +#: templates/admin/feincms/item_editor.html:33 +msgid "available translations" +msgstr "traduceri disponibile" + +#: module/blog/models.py:33 msgid "published" msgstr "publicat" -#: module/blog/models.py:30 +#: module/blog/models.py:36 msgid "This is used for the generated navigation too." msgstr "Aceasta este deasemenea folosită pentru navigarea generată." -#: module/blog/models.py:33 +#: module/blog/models.py:40 msgid "published on" msgstr "publicat la" -#: module/blog/models.py:34 -msgid "" -"Will be set automatically once you tick the `published` checkbox above." -msgstr "Va fi trimis automat în momentul in care bifați butonul `publicat` de mai sus." +#: module/blog/models.py:42 +msgid "Will be set automatically once you tick the `published` checkbox above." +msgstr "" +"Va fi trimis automat în momentul in care bifați butonul `publicat` de mai " +"sus." -#: module/blog/models.py:39 +#: module/blog/models.py:48 msgid "entry" msgstr "înregistrare" -#: module/blog/models.py:40 +#: module/blog/models.py:49 msgid "entries" msgstr "înregistrari" -#: module/blog/extensions/tags.py:12 -msgid "tags" -msgstr "etichete" - -#: module/blog/extensions/translations.py:20 -#: module/extensions/translations.py:126 -msgid "translation of" -msgstr "tradus de" - -#: module/blog/extensions/translations.py:23 -#: module/extensions/translations.py:129 -msgid "Leave this empty for entries in the primary language." -msgstr "" - -#: module/blog/extensions/translations.py:44 -#: templates/admin/feincms/item_editor.html:43 -msgid "available translations" -msgstr "traduceri disponibile" - -#: module/extensions/changedate.py:32 +#: module/extensions/changedate.py:41 msgid "creation date" msgstr "data creerii" -#: module/extensions/changedate.py:33 +#: module/extensions/changedate.py:43 msgid "modification date" msgstr "data modificării" -#: module/extensions/ct_tracker.py:140 +#: module/extensions/ct_tracker.py:156 msgid "content types" msgstr "" -#: module/extensions/datepublisher.py:67 +#: module/extensions/datepublisher.py:86 msgid "publication date" msgstr "data publicării" -#: module/extensions/datepublisher.py:70 +#: module/extensions/datepublisher.py:90 msgid "publication end date" msgstr "sfârșitul publicării" -#: module/extensions/datepublisher.py:72 +#: module/extensions/datepublisher.py:93 msgid "Leave empty if the entry should stay active forever." msgstr "Lasă gol dacă înregistrarea ar trebui să fie activă întotdeauna" -#: module/extensions/datepublisher.py:103 +#: module/extensions/datepublisher.py:128 msgid "visible from - to" msgstr "vizibil de la - până la" -#: module/extensions/datepublisher.py:113 +#: module/extensions/datepublisher.py:139 msgid "Date-based publishing" msgstr "" -#: module/extensions/featured.py:9 +#: module/extensions/featured.py:15 msgid "featured" msgstr "" -#: module/extensions/featured.py:14 +#: module/extensions/featured.py:21 msgid "Featured" msgstr "" -#: module/extensions/seo.py:9 +#: module/extensions/seo.py:16 msgid "meta keywords" msgstr "cuvinte cheie meta" -#: module/extensions/seo.py:10 -msgid "This will be prepended to the default keyword list." -msgstr "Va fi inserat la lista predefinită de cuvinte cheie." +#: module/extensions/seo.py:18 +msgid "Keywords are ignored by most search engines." +msgstr "" -#: module/extensions/seo.py:11 +#: module/extensions/seo.py:20 msgid "meta description" msgstr "descriere meta" -#: module/extensions/seo.py:12 -msgid "This will be prepended to the default description." -msgstr "Va fi inserată la descrierea predefinită." +#: module/extensions/seo.py:22 +msgid "" +"This text is displayed on the search results page. It is however not used " +"for the SEO ranking. Text longer than 140 characters is truncated." +msgstr "" -#: module/extensions/seo.py:17 +#: module/extensions/seo.py:32 msgid "Search engine optimization" msgstr "" -#: module/extensions/translations.py:207 +#: module/extensions/translations.py:249 msgid "Edit translation" msgstr "Modifică translatarea" -#: module/extensions/translations.py:210 +#: module/extensions/translations.py:256 msgid "Create translation" msgstr "Creează o translatare" -#: module/extensions/translations.py:215 +#: module/extensions/translations.py:264 msgid "translations" msgstr "translații" -#: module/medialibrary/forms.py:26 +#: module/medialibrary/forms.py:31 msgid "This would create a loop in the hierarchy" msgstr "" -#: module/medialibrary/forms.py:64 +#: module/medialibrary/forms.py:77 #, python-format msgid "" "Cannot overwrite with different file type (attempt to overwrite a " "%(old_ext)s with a %(new_ext)s)" msgstr "" -#: module/medialibrary/modeladmins.py:58 +#: module/medialibrary/modeladmins.py:63 #, python-format msgid "Successfully added %(count)d media file to %(category)s." msgid_plural "Successfully added %(count)d media files to %(category)s." @@ -448,298 +440,338 @@ msgstr[0] "" msgstr[1] "" msgstr[2] "" -#: module/medialibrary/modeladmins.py:77 +#: module/medialibrary/modeladmins.py:84 msgid "Add selected media files to category" msgstr "" -#: module/medialibrary/modeladmins.py:86 +#: module/medialibrary/modeladmins.py:94 #, python-format msgid "ZIP file exported as %s" msgstr "" -#: module/medialibrary/modeladmins.py:88 +#: module/medialibrary/modeladmins.py:96 #, python-format msgid "ZIP file export failed: %s" msgstr "" -#: module/medialibrary/modeladmins.py:93 +#: module/medialibrary/modeladmins.py:104 msgid "Export selected media files as zip file" msgstr "" -#: module/medialibrary/modeladmins.py:136 +#: module/medialibrary/modeladmins.py:157 #: templates/admin/feincms/page/page/item_editor.html:15 msgid "Preview" msgstr "Pre-vizualizare" -#: module/medialibrary/modeladmins.py:141 module/medialibrary/models.py:88 +#: module/medialibrary/modeladmins.py:162 module/medialibrary/models.py:103 msgid "file size" msgstr "" -#: module/medialibrary/modeladmins.py:146 module/medialibrary/models.py:86 +#: module/medialibrary/modeladmins.py:167 module/medialibrary/models.py:100 msgid "created" msgstr "creat" -#: module/medialibrary/modeladmins.py:165 module/medialibrary/models.py:85 +#: module/medialibrary/modeladmins.py:187 module/medialibrary/models.py:97 msgid "file type" msgstr "tipul fișierului" -#: module/medialibrary/modeladmins.py:186 +#: module/medialibrary/modeladmins.py:211 msgid "file info" msgstr "" -#: module/medialibrary/modeladmins.py:198 +#: module/medialibrary/modeladmins.py:224 #, python-format msgid "%d files imported" msgstr "" -#: module/medialibrary/modeladmins.py:200 +#: module/medialibrary/modeladmins.py:226 #, python-format msgid "ZIP import failed: %s" msgstr "" -#: module/medialibrary/modeladmins.py:202 +#: module/medialibrary/modeladmins.py:228 msgid "No input file given" msgstr "" -#: module/medialibrary/models.py:43 +#: module/medialibrary/models.py:49 msgid "parent" msgstr "părinte" -#: module/medialibrary/models.py:45 module/page/models.py:171 +#: module/medialibrary/models.py:51 module/page/models.py:175 msgid "slug" msgstr "slug" -#: module/medialibrary/models.py:49 +#: module/medialibrary/models.py:55 msgid "category" msgstr "categorie" -#: module/medialibrary/models.py:50 module/medialibrary/models.py:90 +#: module/medialibrary/models.py:56 module/medialibrary/models.py:106 msgid "categories" msgstr "categorii" -#: module/medialibrary/models.py:87 +#: module/medialibrary/models.py:101 msgid "copyright" msgstr "copyright" -#: module/medialibrary/models.py:202 +#: module/medialibrary/models.py:219 msgid "Image" msgstr "Imagine" -#: module/medialibrary/models.py:203 +#: module/medialibrary/models.py:221 msgid "Video" msgstr "" -#: module/medialibrary/models.py:204 +#: module/medialibrary/models.py:224 msgid "Audio" msgstr "" -#: module/medialibrary/models.py:205 +#: module/medialibrary/models.py:226 msgid "PDF document" msgstr "document PDF" -#: module/medialibrary/models.py:206 +#: module/medialibrary/models.py:227 msgid "Flash" msgstr "Flash" -#: module/medialibrary/models.py:207 +#: module/medialibrary/models.py:228 msgid "Text" msgstr "Text" -#: module/medialibrary/models.py:208 +#: module/medialibrary/models.py:229 msgid "Rich Text" msgstr "" -#: module/medialibrary/models.py:209 +#: module/medialibrary/models.py:230 msgid "Zip archive" msgstr "" -#: module/medialibrary/models.py:210 +#: module/medialibrary/models.py:231 msgid "Microsoft Word" msgstr "" -#: module/medialibrary/models.py:211 +#: module/medialibrary/models.py:233 msgid "Microsoft Excel" msgstr "" -#: module/medialibrary/models.py:212 +#: module/medialibrary/models.py:235 msgid "Microsoft PowerPoint" msgstr "" -#: module/medialibrary/models.py:213 +#: module/medialibrary/models.py:237 msgid "Binary" msgstr "Binar" -#: module/medialibrary/models.py:232 +#: module/medialibrary/models.py:261 msgid "description" msgstr "descriere" -#: module/medialibrary/models.py:235 +#: module/medialibrary/models.py:264 msgid "media file translation" msgstr "traducere fișier media" -#: module/medialibrary/models.py:236 +#: module/medialibrary/models.py:265 msgid "media file translations" msgstr "traduceri fișier media" -#: module/page/forms.py:172 +#: module/page/extensions/excerpt.py:18 +msgid "excerpt" +msgstr "" + +#: module/page/extensions/excerpt.py:21 +msgid "Add a brief excerpt summarizing the content of this page." +msgstr "" + +#: module/page/extensions/excerpt.py:25 +msgid "Excerpt" +msgstr "" + +#: module/page/extensions/navigation.py:86 +#: module/page/extensions/navigation.py:115 +msgid "navigation extension" +msgstr "navigare extinsă" + +#: module/page/extensions/navigation.py:119 +msgid "" +"Select the module providing subpages for this page if you need to customize " +"the navigation." +msgstr "" +"Selectați modulul care furnizează subpaginile pentru această pagină dacă " +"doriți să personalizaţi." + +#: module/page/extensions/navigation.py:134 +msgid "Navigation extension" +msgstr "" + +#: module/page/extensions/navigationgroups.py:20 +msgid "Default" +msgstr "" + +#: module/page/extensions/navigationgroups.py:21 +msgid "Footer" +msgstr "" + +#: module/page/extensions/navigationgroups.py:28 +#, fuzzy +#| msgid "in navigation" +msgid "navigation group" +msgstr "în navigare" + +#: module/page/extensions/relatedpages.py:21 +msgid "Select pages that should be listed as related content." +msgstr "" + +#: module/page/extensions/relatedpages.py:26 +msgid "Related pages" +msgstr "" + +#: module/page/extensions/sites.py:21 +msgid "Site" +msgstr "" + +#: module/page/extensions/symlinks.py:22 +msgid "symlinked page" +msgstr "legătură pagină" + +#: module/page/extensions/symlinks.py:23 +msgid "All content is inherited from this page if given." +msgstr "Întreg conținutul este moștenit de la această pagină daca se dorește." + +#: module/page/extensions/titles.py:19 +msgid "content title" +msgstr "titlu conținut" + +#: module/page/extensions/titles.py:22 +msgid "The first line is the main title, the following lines are subtitles." +msgstr "Prima linie reprezintă titlul principal, următoarele sunt subtitluri." + +#: module/page/extensions/titles.py:26 +msgid "page title" +msgstr "titlul paginii" + +#: module/page/extensions/titles.py:30 +#, fuzzy +#| msgid "Page title for browser window. Same as title by default." +msgid "" +"Page title for browser window. Same as title bydefault. Must not be longer " +"than 70 characters." +msgstr "" +"Titlul paginii pentru fereastra navigatorului. Implicit este la fel cu " +"titlul paginii." + +#: module/page/extensions/titles.py:60 +msgid "Titles" +msgstr "" + +#: module/page/forms.py:187 msgid "This URL is already taken by an active page." msgstr "Acest URL este deja rezervat de către o pagină activă." -#: module/page/forms.py:190 +#: module/page/forms.py:206 msgid "This URL is already taken by another active page." msgstr "Acest URL este deja rezervat de către o altă pagină activă." -#: module/page/modeladmins.py:41 +#: module/page/forms.py:211 +msgid "This page does not allow attachment of child pages" +msgstr "" + +#: module/page/modeladmins.py:42 msgid "Other options" msgstr "Alte opțiuni" -#: module/page/modeladmins.py:90 module/page/models.py:174 +#: module/page/modeladmins.py:80 module/page/models.py:182 msgid "in navigation" msgstr "în navigare" -#: module/page/modeladmins.py:105 +#: module/page/modeladmins.py:109 module/page/modeladmins.py:111 msgid "Add child page" msgstr "Adaugă o pagină copil" -#: module/page/modeladmins.py:107 +#: module/page/modeladmins.py:120 module/page/modeladmins.py:122 #: templates/admin/feincms/content_inline.html:9 msgid "View on site" msgstr "Vezi pe site" -#: module/page/modeladmins.py:126 +#: module/page/modeladmins.py:142 #, python-format -msgid "Add %(language)s Translation of \"%(page)s\"" +msgid "Add %(language)s translation of \"%(page)s\"" msgstr "" -#: module/page/modeladmins.py:156 +#: module/page/modeladmins.py:177 msgid "" "The content from the original translation has been copied to the newly " "created page." msgstr "" -#: module/page/modeladmins.py:170 +#: module/page/modeladmins.py:197 msgid "You don't have the necessary permissions to edit this object" msgstr "" -#: module/page/modeladmins.py:185 +#: module/page/modeladmins.py:223 msgid "inherited" msgstr "moștenit" -#: module/page/modeladmins.py:189 +#: module/page/modeladmins.py:229 msgid "extensions" msgstr "extensii" -#: module/page/modeladmins.py:193 module/page/models.py:206 +#: module/page/modeladmins.py:233 module/page/models.py:221 msgid "is active" msgstr "" -#: module/page/models.py:167 +#: module/page/models.py:169 msgid "active" msgstr "activ" -#: module/page/models.py:175 +#: module/page/models.py:173 +#, fuzzy +#| msgid "This is used for the generated navigation too." +msgid "This title is also used for navigation menu items." +msgstr "Aceasta este deasemenea folosită pentru navigarea generată." + +#: module/page/models.py:176 +msgid "This is used to build the URL for this page" +msgstr "" + +#: module/page/models.py:184 msgid "override URL" msgstr "suprascrie URL" -#: module/page/models.py:176 +#: module/page/models.py:186 msgid "" "Override the target URL. Be sure to include slashes at the beginning and at " -"the end if it is a local URL. This affects both the navigation and subpages'" -" URLs." -msgstr "Suprascrie URL-ul țintă. Aveți grijă să scrieti / la început și la șfârșit dacă este un URL local.Aceasta afectează atât navigația cât și subpaginile." +"the end if it is a local URL. This affects both the navigation and subpages' " +"URLs." +msgstr "" +"Suprascrie URL-ul țintă. Aveți grijă să scrieti / la început și la șfârșit " +"dacă este un URL local.Aceasta afectează atât navigația cât și subpaginile." -#: module/page/models.py:177 +#: module/page/models.py:190 msgid "redirect to" msgstr "redirecționare către" -#: module/page/models.py:178 +#: module/page/models.py:193 msgid "Target URL for automatic redirects or the primary key of a page." msgstr "" -#: module/page/models.py:180 +#: module/page/models.py:196 msgid "Cached URL" msgstr "URL în cache" -#: module/page/models.py:395 +#: module/page/models.py:298 +#, python-format +msgid "" +"This %(page_class)s uses a singleton template, and " +"FEINCMS_SINGLETON_TEMPLATE_DELETION_ALLOWED=False" +msgstr "" + +#: module/page/models.py:426 msgid "page" msgstr "pagină" -#: module/page/models.py:396 +#: module/page/models.py:427 msgid "pages" msgstr "pagini" -#: module/page/extensions/excerpt.py:9 -msgid "excerpt" -msgstr "" - -#: module/page/extensions/excerpt.py:10 -msgid "Add a brief excerpt summarizing the content of this page." -msgstr "" - -#: module/page/extensions/excerpt.py:12 -msgid "Excerpt" -msgstr "" - -#: module/page/extensions/navigation.py:82 -#: module/page/extensions/navigation.py:107 -msgid "navigation extension" -msgstr "navigare extinsă" - -#: module/page/extensions/navigation.py:110 -msgid "" -"Select the module providing subpages for this page if you need to customize " -"the navigation." -msgstr "Selectați modulul care furnizează subpaginile pentru această pagină dacă doriți să personalizaţi." - -#: module/page/extensions/navigation.py:125 -msgid "Navigation extension" -msgstr "" - -#: module/page/extensions/relatedpages.py:14 -msgid "Select pages that should be listed as related content." -msgstr "" - -#: module/page/extensions/relatedpages.py:19 -msgid "Related pages" -msgstr "" - -#: module/page/extensions/sites.py:16 -msgid "Site" -msgstr "" - -#: module/page/extensions/symlinks.py:15 -msgid "symlinked page" -msgstr "legătură pagină" - -#: module/page/extensions/symlinks.py:16 -msgid "All content is inherited from this page if given." -msgstr "Întreg conținutul este moștenit de la această pagină daca se dorește." - -#: module/page/extensions/titles.py:13 -msgid "content title" -msgstr "titlu conținut" - -#: module/page/extensions/titles.py:14 -msgid "The first line is the main title, the following lines are subtitles." -msgstr "Prima linie reprezintă titlul principal, următoarele sunt subtitluri." - -#: module/page/extensions/titles.py:15 -msgid "page title" -msgstr "titlul paginii" - -#: module/page/extensions/titles.py:16 -msgid "Page title for browser window. Same as title by default." -msgstr "Titlul paginii pentru fereastra navigatorului. Implicit este la fel cu titlul paginii." - -#: module/page/extensions/titles.py:43 -msgid "Titles" -msgstr "" - -#: templates/admin/filter.html:3 -#, python-format -msgid " By %(filter_title)s " -msgstr "După %(filter_title)s" - #: templates/admin/feincms/_messages_js.html:4 msgid "Really delete item?" msgstr "Confirmți ștergerea obiectului?" @@ -758,7 +790,9 @@ msgstr "Nu pot șterge obiectul" #: templates/admin/feincms/_messages_js.html:6 msgid "Cannot delete item, because it is parent of at least one other item." -msgstr "Nu se poate șterge obiectul, deoarece este părinte la cel puțin incă un obiect." +msgstr "" +"Nu se poate șterge obiectul, deoarece este părinte la cel puțin incă un " +"obiect." #: templates/admin/feincms/_messages_js.html:7 msgid "Change template" @@ -771,9 +805,8 @@ msgstr "Doriți ștergerea șablonului?
    Toate modificarile au fost salvate #: templates/admin/feincms/_messages_js.html:9 #, python-format msgid "" -"Really change template?
    All changes are saved and content from " -"%%(source_regions)s is moved to " -"%%(target_region)s." +"Really change template?
    All changes are saved and content from " +"%%(source_regions)s is moved to %%(target_region)s." msgstr "" #: templates/admin/feincms/_messages_js.html:12 @@ -808,22 +841,24 @@ msgstr "Regiunea este goală" msgid "" "Content from the parent site is automatically inherited. To override this " "behaviour, add some content." -msgstr "Conținutul de la pagina părinte este moștenită automat. Pentru a suprascrie acest lucru, adăugați conținut" +msgstr "" +"Conținutul de la pagina părinte este moștenită automat. Pentru a suprascrie " +"acest lucru, adăugați conținut" #: templates/admin/feincms/content_editor.html:33 msgid "Add new item" msgstr "Adaugă un obiect nou" -#: templates/admin/feincms/content_inline.html:91 +#: templates/admin/feincms/content_inline.html:93 #, python-format msgid "Add another %(verbose_name)s" msgstr "" -#: templates/admin/feincms/content_inline.html:94 +#: templates/admin/feincms/content_inline.html:96 msgid "Remove" msgstr "" -#: templates/admin/feincms/fe_editor.html:40 +#: templates/admin/feincms/fe_editor.html:30 msgid "Save" msgstr "Salvează" @@ -851,13 +886,21 @@ msgstr "jos" msgid "remove" msgstr "scoate" -#: templates/admin/feincms/recover_form.html:8 -#: templates/admin/feincms/revision_form.html:8 +#: templates/admin/feincms/page/page/item_editor.html:10 +msgid "Edit on site" +msgstr "" + #: templates/admin/feincms/page/page/item_editor.html:23 #: templates/admin/feincms/page/page/tree_editor.html:7 +#: templates/admin/feincms/recover_form.html:8 +#: templates/admin/feincms/revision_form.html:8 msgid "Home" msgstr "Acasă" +#: templates/admin/feincms/page/page/item_editor.html:27 +msgid "Add" +msgstr "" + #: templates/admin/feincms/recover_form.html:11 #, python-format msgid "Recover deleted %(verbose_name)s" @@ -880,29 +923,26 @@ msgstr "" msgid "Press the save button below to revert to this version of the object." msgstr "" -#: templates/admin/feincms/tree_editor.html:32 +#: templates/admin/feincms/tree_editor.html:22 msgid "Shortcuts" msgstr "Scurtături" -#: templates/admin/feincms/tree_editor.html:34 +#: templates/admin/feincms/tree_editor.html:24 msgid "Collapse tree" msgstr "Închide arborele" -#: templates/admin/feincms/tree_editor.html:35 +#: templates/admin/feincms/tree_editor.html:25 msgid "Expand tree" msgstr "Deschide arborele" -#: templates/admin/feincms/tree_editor.html:38 +#: templates/admin/feincms/tree_editor.html:29 msgid "Filter" msgstr "Filtrează" -#: templates/admin/feincms/page/page/item_editor.html:10 -msgid "Edit on site" -msgstr "" - -#: templates/admin/feincms/page/page/item_editor.html:27 -msgid "Add" -msgstr "" +#: templates/admin/filter.html:3 +#, python-format +msgid " By %(filter_title)s " +msgstr "După %(filter_title)s" #: templates/admin/medialibrary/add_to_category.html:5 #: templates/admin/medialibrary/add_to_category.html:9 @@ -946,7 +986,8 @@ msgstr "" #, python-format msgid "" "\n" -" %(comment_username)s said on %(comment_submit_date)s
    \n" +" %(comment_username)s said on %(comment_submit_date)s
    \n" " " msgstr "" @@ -965,3 +1006,27 @@ msgstr "Trimite" #: templates/content/contactform/thanks.html:3 msgid "Thanks!" msgstr "Mulțumesc!" + +#~ msgid "plain" +#~ msgstr "simplu" + +#~ msgid "title row" +#~ msgstr "titlu" + +#~ msgid "title row and column" +#~ msgstr "titlu rând și coloană" + +#~ msgid "table" +#~ msgstr "tabel" + +#~ msgid "tables" +#~ msgstr "tabele" + +#~ msgid "data" +#~ msgstr "data" + +#~ msgid "This will be prepended to the default keyword list." +#~ msgstr "Va fi inserat la lista predefinită de cuvinte cheie." + +#~ msgid "This will be prepended to the default description." +#~ msgstr "Va fi inserată la descrierea predefinită." diff --git a/feincms/locale/ru/LC_MESSAGES/django.mo b/feincms/locale/ru/LC_MESSAGES/django.mo index 219424a75..2a4485338 100644 Binary files a/feincms/locale/ru/LC_MESSAGES/django.mo and b/feincms/locale/ru/LC_MESSAGES/django.mo differ diff --git a/feincms/locale/ru/LC_MESSAGES/django.po b/feincms/locale/ru/LC_MESSAGES/django.po index ef920ccd0..a6dbeea9a 100644 --- a/feincms/locale/ru/LC_MESSAGES/django.po +++ b/feincms/locale/ru/LC_MESSAGES/django.po @@ -1,17 +1,18 @@ -# SOME DESCRIPTIVE TITLE. -# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER -# This file is distributed under the same license as the PACKAGE package. +# Feincms Russian translation file +# Copyright (C) 2017 +# This file is distributed under the same license as the feincms package. # # Translators: # Mikhail Korobov , 2009 # Denis Popov , 2014 +# Alexander Paramonov , 2017 msgid "" msgstr "" "Project-Id-Version: FeinCMS\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2014-05-22 20:55-0500\n" +"POT-Creation-Date: 2017-10-24 17:10+0300\n" "PO-Revision-Date: 2013-10-25 09:15+0000\n" -"Last-Translator: Matthias Kestenholz \n" +"Last-Translator: Alexander Paramonov \n" "Language-Team: Russian (http://www.transifex.com/projects/p/feincms/language/" "ru/)\n" "Language: ru\n" @@ -21,144 +22,125 @@ msgstr "" "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" -#: models.py:390 content/template/models.py:63 -msgid "template" -msgstr "шаблон" - -#: models.py:538 -msgid "ordering" -msgstr "сортировка" - -#: translations.py:282 module/blog/extensions/translations.py:25 -#: module/extensions/translations.py:140 -msgid "language" -msgstr "язык" - -#: admin/filterspecs.py:47 admin/filterspecs.py:86 +#: admin/filters.py:49 admin/filters.py:100 msgid "All" msgstr "Все" -#: admin/filterspecs.py:58 module/page/models.py:178 +#: admin/filters.py:60 module/page/models.py:165 msgid "Parent" msgstr "Родитель" -#: admin/filterspecs.py:97 -#: templates/admin/medialibrary/mediafile/change_list.html:14 +#: admin/filters.py:111 +#: templates/admin/medialibrary/mediafile/change_list.html:13 msgid "Category" msgstr "Категория" -#: admin/item_editor.py:189 -#, python-format -msgid "Change %s" -msgstr "Изменить %s" - -#: admin/tree_editor.py:293 content/rss/models.py:23 -#: content/section/models.py:35 module/blog/models.py:34 -#: module/medialibrary/models.py:45 module/page/models.py:172 -#: module/page/models.py:240 +#: admin/tree_editor.py:281 content/section/models.py:35 +#: module/medialibrary/models.py:45 module/page/models.py:159 +#: module/page/models.py:232 msgid "title" msgstr "заголовок" -#: admin/tree_editor.py:352 admin/tree_editor.py:369 +#: admin/tree_editor.py:328 admin/tree_editor.py:345 msgid "You do not have permission to modify this object" msgstr "У вас нет прав для изменения этого объекта" -#: admin/tree_editor.py:486 +#: admin/tree_editor.py:461 msgid "No permission" msgstr "Нет прав" -#: admin/tree_editor.py:504 +#: admin/tree_editor.py:479 #, python-format msgid "%s has been moved to a new position." msgstr "Узел \"%s\" был перемещен на новое место." -#: admin/tree_editor.py:507 +#: admin/tree_editor.py:482 msgid "Did not understand moving instruction." msgstr "Перемещение не удалось. Непонятная команда." -#: admin/tree_editor.py:518 +#: admin/tree_editor.py:492 msgid "actions" msgstr "действия" -#: admin/tree_editor.py:541 -#, fuzzy, python-format +#: admin/tree_editor.py:521 +#, python-format msgid "Successfully deleted %(count)d items." -msgstr "Успешно удалено %s объектов" +msgstr "Успешно удалено %(count)d объектов" -#: admin/tree_editor.py:554 +#: admin/tree_editor.py:534 #, python-format msgid "Delete selected %(verbose_name_plural)s" msgstr "Удалить выбранные %(verbose_name_plural)s" -#: content/application/models.py:147 +#: content/application/models.py:185 msgid "application content" msgstr "Данные приложения" -#: content/application/models.py:148 +#: content/application/models.py:186 msgid "application contents" msgstr "Данные приложений" -#: content/application/models.py:179 +#: content/application/models.py:217 msgid "application" msgstr "приложение" -#: content/comments/models.py:31 -msgid "enabled" -msgstr "включены" - -#: content/comments/models.py:32 -msgid "New comments may be added" -msgstr "Новые комментарии могут быть добавлены" - -#: content/comments/models.py:36 content/comments/models.py:37 -msgid "comments" -msgstr "комментарии" - -#: content/comments/models.py:59 -msgid "public" -msgstr "Опубликовано" - -#: content/comments/models.py:60 -msgid "not public" -msgstr "Не опубликовано" - -#: content/contactform/models.py:20 +#: content/contactform/models.py:21 msgid "name" msgstr "имя" -#: content/contactform/models.py:21 +#: content/contactform/models.py:22 msgid "email" msgstr "email" -#: content/contactform/models.py:22 +#: content/contactform/models.py:23 msgid "subject" msgstr "тема" -#: content/contactform/models.py:26 content/raw/models.py:16 +#: content/contactform/models.py:27 content/raw/models.py:16 msgid "content" msgstr "содержание" -#: content/contactform/models.py:37 +#: content/contactform/models.py:38 msgid "contact form" msgstr "форма обратной связи" -#: content/contactform/models.py:38 +#: content/contactform/models.py:39 msgid "contact forms" msgstr "формы обратной связи" #: content/file/models.py:23 content/file/models.py:28 -#: module/medialibrary/models.py:94 +#: content/filer/models.py:50 content/filer/models.py:56 +#: module/medialibrary/models.py:96 msgid "file" msgstr "файл" -#: content/file/models.py:29 +#: content/file/models.py:29 content/filer/models.py:57 msgid "files" msgstr "файлы" +#: content/filer/models.py:86 content/filer/models.py:102 #: content/image/models.py:46 content/image/models.py:55 msgid "image" msgstr "картинка" +#: content/filer/models.py:88 content/image/models.py:51 +#: module/medialibrary/models.py:263 +msgid "caption" +msgstr "подпись" + +#: content/filer/models.py:93 +msgid "URL" +msgstr "" + +#: content/filer/models.py:103 content/image/models.py:56 +msgid "images" +msgstr "картинки" + +#: content/filer/models.py:115 content/section/models.py:59 +#: module/medialibrary/contents.py:60 +msgid "type" +msgstr "тип" + #: content/image/models.py:49 msgid "alternate text" msgstr "alt текст" @@ -167,35 +149,14 @@ msgstr "alt текст" msgid "Description of image" msgstr "Описание изображения" -#: content/image/models.py:51 module/medialibrary/models.py:260 -msgid "caption" -msgstr "подпись" - -#: content/image/models.py:56 -msgid "images" -msgstr "картинки" - -#: content/image/models.py:82 +#: content/image/models.py:84 msgid "position" msgstr "расположение" -#: content/image/models.py:90 +#: content/image/models.py:92 msgid "format" msgstr "формат" -#: content/medialibrary/models.py:51 content/section/models.py:38 -#: module/medialibrary/fields.py:66 module/medialibrary/models.py:112 -msgid "media file" -msgstr "медиа-файл" - -#: content/medialibrary/models.py:52 module/medialibrary/models.py:113 -msgid "media files" -msgstr "медиа-файлы" - -#: content/medialibrary/models.py:64 content/section/models.py:59 -msgid "type" -msgstr "тип" - #: content/raw/models.py:20 msgid "raw content" msgstr "HTML данные" @@ -204,64 +165,22 @@ msgstr "HTML данные" msgid "raw contents" msgstr "HTML данные" -#: content/richtext/models.py:24 -msgid "HTML Tidy" -msgstr "HTML Tidy" - -#: content/richtext/models.py:25 -msgid "Ignore the HTML validation warnings" -msgstr "Игнорировать предупреждения при валидации HTML" - -#: content/richtext/models.py:55 -#, python-format -msgid "" -"HTML validation produced %(count)d warnings. Please review the updated " -"content below before continuing: %(messages)s" -msgstr "" -"HTML валидация вызвала %(count)d предупреждений. Пожалуйста просмотрите ниже обновленное " -"содержимое прежде чем продолжить: %(messages)s" -#: content/richtext/models.py:99 content/section/models.py:36 -msgid "text" -msgstr "текст" - -#: content/richtext/models.py:103 +#: content/richtext/models.py:33 msgid "rich text" msgstr "форматированный текст" -#: content/richtext/models.py:104 +#: content/richtext/models.py:34 msgid "rich texts" msgstr "форматированные тексты" -#: content/rss/models.py:25 -msgid "" -"The rss field is updated several times a day. A change in the title will " -"only be visible on the home page after the next feed update." -msgstr "" -"RSS поле обновляется несколько раз в день. Изменение в названии будет видно " -"на главной странице только после следующего обновления фида" -#: content/rss/models.py:28 -msgid "link" -msgstr "ссылка" - -#: content/rss/models.py:30 -msgid "pre-rendered content" -msgstr "предподготовленное содержимое" - -#: content/rss/models.py:32 -msgid "last updated" -msgstr "последнее обновление" - -#: content/rss/models.py:33 -msgid "max. items" -msgstr "макс. число элементов" - -#: content/rss/models.py:37 -msgid "RSS feed" -msgstr "RSS фид" +#: content/richtext/models.py:48 content/section/models.py:36 +msgid "text" +msgstr "текст" -#: content/rss/models.py:38 -msgid "RSS feeds" -msgstr "RSS фиды" +#: content/section/models.py:38 module/medialibrary/contents.py:47 +#: module/medialibrary/fields.py:72 module/medialibrary/models.py:114 +msgid "media file" +msgstr "медиа-файл" #: content/section/models.py:43 msgid "section" @@ -271,19 +190,23 @@ msgstr "секция" msgid "sections" msgstr "секции" -#: content/template/models.py:53 +#: content/template/models.py:24 msgid "template content" msgstr "содержимое шаблона" -#: content/template/models.py:54 +#: content/template/models.py:25 msgid "template contents" msgstr "содержимое шаблонов" -#: content/video/models.py:34 +#: content/template/models.py:30 models.py:396 +msgid "template" +msgstr "шаблон" + +#: content/video/models.py:35 msgid "video link" msgstr "ссылка на видео" -#: content/video/models.py:36 +#: content/video/models.py:37 msgid "" "This should be a link to a youtube or vimeo video, i.e.: http://www.youtube." "com/watch?v=zmj1rpzDRZ0" @@ -291,151 +214,128 @@ msgstr "" "Это должно быть ссылкой на видео, размещенное на youtube или vimeo, " "например, http://www.youtube.com/watch?v=zmj1rpzDRZ0" -#: content/video/models.py:41 +#: content/video/models.py:42 msgid "video" msgstr "видео" -#: content/video/models.py:42 +#: content/video/models.py:43 msgid "videos" msgstr "видео" -#: contrib/tagging.py:132 +#: contrib/tagging.py:139 msgid "Tagging" -msgstr "Теггирование" - -#: module/blog/models.py:32 -msgid "published" -msgstr "опубликовано" - -#: module/blog/models.py:35 -msgid "This is used for the generated navigation too." -msgstr "Используется также в сгенерированно навигации." - -#: module/blog/models.py:39 -msgid "published on" -msgstr "опубликовано" - -#: module/blog/models.py:41 -msgid "Will be set automatically once you tick the `published` checkbox above." -msgstr "" -"Будет установлено автоматически, как только Вы отметите пункт \"опубликовано" -"\" выше." - -#: module/blog/models.py:47 -msgid "entry" -msgstr "запись" - -#: module/blog/models.py:48 -msgid "entries" -msgstr "записи" - -#: module/blog/extensions/tags.py:17 -msgid "tags" -msgstr "теги" - -#: module/blog/extensions/translations.py:35 -#: module/extensions/translations.py:148 -msgid "translation of" -msgstr "перевод" - -#: module/blog/extensions/translations.py:39 -#: module/extensions/translations.py:152 -msgid "Leave this empty for entries in the primary language." -msgstr "Оставьте поле пустым для записей на основном языке (%s)." - -#: module/blog/extensions/translations.py:67 -#: templates/admin/feincms/item_editor.html:33 -msgid "available translations" -msgstr "доступные переводы" +msgstr "Тегирование" -#: module/extensions/changedate.py:41 +#: extensions/changedate.py:42 msgid "creation date" msgstr "дата создания" -#: module/extensions/changedate.py:43 +#: extensions/changedate.py:44 msgid "modification date" msgstr "дата изменения" -#: module/extensions/ct_tracker.py:156 +#: extensions/ct_tracker.py:155 msgid "content types" msgstr "типы данных" -#: module/extensions/datepublisher.py:86 +#: extensions/datepublisher.py:99 msgid "publication date" msgstr "дата публикации" -#: module/extensions/datepublisher.py:90 +#: extensions/datepublisher.py:103 msgid "publication end date" msgstr "дата окончания публикации" -#: module/extensions/datepublisher.py:93 +#: extensions/datepublisher.py:106 msgid "Leave empty if the entry should stay active forever." msgstr "Оставьте поле пустым, если запись должна оставаться активной навсегда." -#: module/extensions/datepublisher.py:128 +#: extensions/datepublisher.py:141 msgid "visible from - to" msgstr "отображается с - по" -#: module/extensions/datepublisher.py:139 +#: extensions/datepublisher.py:152 msgid "Date-based publishing" msgstr "Дато-ориентированная публикация" -#: module/extensions/featured.py:15 +#: extensions/featured.py:18 msgid "featured" msgstr "избранное" -#: module/extensions/featured.py:21 +#: extensions/featured.py:24 msgid "Featured" msgstr "Избранное" -#: module/extensions/seo.py:16 +#: extensions/seo.py:16 msgid "meta keywords" -msgstr "Ключевые слова" +msgstr "Ключевые слова (meta keywords)" -#: module/extensions/seo.py:18 +#: extensions/seo.py:18 msgid "Keywords are ignored by most search engines." -msgstr "Ключевые слова игнорируют большинство поисковиков" +msgstr "Ключевые слова игнорируются большинством поисковиков" -#: module/extensions/seo.py:20 +#: extensions/seo.py:20 msgid "meta description" -msgstr "Описание" +msgstr "Описание (meta description)" -#: module/extensions/seo.py:22 +#: extensions/seo.py:22 msgid "" "This text is displayed on the search results page. It is however not used " "for the SEO ranking. Text longer than 140 characters is truncated." msgstr "" -"Этот текст отображается в выдаче поисковиков. Но оно не используется " -"для SEO ранжирования. Текст длиннее 140 символов усекается." -#: module/extensions/seo.py:32 +"Этот текст отображается в выдаче поисковиков. Но он не используется для SEO " +"ранжирования. Текст длиннее 140 символов усекается." + +#: extensions/seo.py:32 msgid "Search engine optimization" -msgstr "SEO Оптимизация" +msgstr "Поисковая оптимизация (SEO)" + +#: extensions/translations.py:162 translations.py:283 +msgid "language" +msgstr "язык" -#: module/extensions/translations.py:239 +#: extensions/translations.py:171 +msgid "translation of" +msgstr "перевод" + +#: extensions/translations.py:175 +msgid "Leave this empty for entries in the primary language." +msgstr "Оставьте поле пустым для записей на основном языке." + +#: extensions/translations.py:284 msgid "Edit translation" msgstr "Изменить перевод" -#: module/extensions/translations.py:246 +#: extensions/translations.py:291 msgid "Create translation" msgstr "Создать перевод" -#: module/extensions/translations.py:254 +#: extensions/translations.py:298 msgid "translations" msgstr "переводы" -#: module/medialibrary/forms.py:28 +#: models.py:524 +msgid "ordering" +msgstr "сортировка" + +#: module/medialibrary/contents.py:48 module/medialibrary/models.py:115 +msgid "media files" +msgstr "медиа-файлы" + +#: module/medialibrary/forms.py:29 msgid "This would create a loop in the hierarchy" msgstr "Это создало бы зацикливание в иерархии" -#: module/medialibrary/forms.py:72 +#: module/medialibrary/forms.py:74 #, python-format msgid "" "Cannot overwrite with different file type (attempt to overwrite a " "%(old_ext)s with a %(new_ext)s)" msgstr "" -"Невозможно перезаписать с различным типом файла (попытка перезаписать " +"Невозможно перезаписать с другим типом файла (попытка перезаписать " "%(old_ext)s с %(new_ext)s)" -#: module/medialibrary/modeladmins.py:63 + +#: module/medialibrary/modeladmins.py:65 #, python-format msgid "Successfully added %(count)d media file to %(category)s." msgid_plural "Successfully added %(count)d media files to %(category)s." @@ -443,139 +343,224 @@ msgstr[0] "%(count)d медиа-файл успешно добавлен в %(ca msgstr[1] "%(count)d медиа-файла успешно добавлены в %(category)s." msgstr[2] "%(count)d медиа-файлов успешно добавлены в %(category)s." -#: module/medialibrary/modeladmins.py:84 +#: module/medialibrary/modeladmins.py:86 msgid "Add selected media files to category" msgstr "Добавить выбранные медиа-файлы в категорию" -#: module/medialibrary/modeladmins.py:94 +#: module/medialibrary/modeladmins.py:96 #, python-format msgid "ZIP file exported as %s" msgstr "ZIP файл экспортирован как %s" -#: module/medialibrary/modeladmins.py:96 +#: module/medialibrary/modeladmins.py:98 #, python-format msgid "ZIP file export failed: %s" msgstr "Экспорт ZIP файла не удался: %s" -#: module/medialibrary/modeladmins.py:104 +#: module/medialibrary/modeladmins.py:106 msgid "Export selected media files as zip file" msgstr "Экспортировать выбранные медиа-файлы как ZIP архив" -#: module/medialibrary/modeladmins.py:157 -#: templates/admin/feincms/page/page/item_editor.html:15 +#: module/medialibrary/modeladmins.py:155 +#: templates/admin/feincms/page/page/item_editor.html:11 msgid "Preview" msgstr "Предпросмотр" -#: module/medialibrary/modeladmins.py:162 module/medialibrary/models.py:103 +#: module/medialibrary/modeladmins.py:159 module/medialibrary/models.py:105 msgid "file size" msgstr "размер файла" -#: module/medialibrary/modeladmins.py:167 module/medialibrary/models.py:100 +#: module/medialibrary/modeladmins.py:164 module/medialibrary/models.py:102 msgid "created" msgstr "создан" -#: module/medialibrary/modeladmins.py:187 module/medialibrary/models.py:97 +#: module/medialibrary/modeladmins.py:184 module/medialibrary/models.py:99 msgid "file type" msgstr "тип файла" -#: module/medialibrary/modeladmins.py:211 +#: module/medialibrary/modeladmins.py:207 msgid "file info" msgstr "информация о файле" -#: module/medialibrary/modeladmins.py:224 +#: module/medialibrary/modeladmins.py:219 #, python-format msgid "%d files imported" msgstr "%d файлов импортировано" -#: module/medialibrary/modeladmins.py:226 +#: module/medialibrary/modeladmins.py:221 #, python-format msgid "ZIP import failed: %s" msgstr "ZIP импорт не удался: %s" -#: module/medialibrary/modeladmins.py:228 +#: module/medialibrary/modeladmins.py:223 msgid "No input file given" msgstr "Не получен входной файл" -#: module/medialibrary/models.py:49 +#: module/medialibrary/models.py:50 msgid "parent" msgstr "родитель" -#: module/medialibrary/models.py:51 module/page/models.py:175 +#: module/medialibrary/models.py:52 module/page/models.py:162 msgid "slug" -msgstr "путь в URL" +msgstr "слаг" -#: module/medialibrary/models.py:55 +#: module/medialibrary/models.py:56 msgid "category" msgstr "категория" -#: module/medialibrary/models.py:56 module/medialibrary/models.py:106 +#: module/medialibrary/models.py:57 module/medialibrary/models.py:108 msgid "categories" msgstr "категории" -#: module/medialibrary/models.py:101 +#: module/medialibrary/models.py:103 msgid "copyright" msgstr "копирайт" -#: module/medialibrary/models.py:219 +#: module/medialibrary/models.py:221 msgid "Image" msgstr "Изображение" -#: module/medialibrary/models.py:221 +#: module/medialibrary/models.py:223 msgid "Video" msgstr "Видео" -#: module/medialibrary/models.py:224 +#: module/medialibrary/models.py:226 msgid "Audio" msgstr "Аудио" -#: module/medialibrary/models.py:226 +#: module/medialibrary/models.py:228 msgid "PDF document" msgstr "Документ PDF" -#: module/medialibrary/models.py:227 +#: module/medialibrary/models.py:229 msgid "Flash" msgstr "Флэш" -#: module/medialibrary/models.py:228 +#: module/medialibrary/models.py:230 msgid "Text" msgstr "Текст" -#: module/medialibrary/models.py:229 +#: module/medialibrary/models.py:231 msgid "Rich Text" msgstr "Форматтированный текст" -#: module/medialibrary/models.py:230 +#: module/medialibrary/models.py:232 msgid "Zip archive" msgstr "Zip архив" -#: module/medialibrary/models.py:231 +#: module/medialibrary/models.py:233 msgid "Microsoft Word" msgstr "" -#: module/medialibrary/models.py:233 +#: module/medialibrary/models.py:235 msgid "Microsoft Excel" msgstr "" -#: module/medialibrary/models.py:235 +#: module/medialibrary/models.py:237 msgid "Microsoft PowerPoint" msgstr "" -#: module/medialibrary/models.py:237 +#: module/medialibrary/models.py:239 msgid "Binary" msgstr "Двоичный" -#: module/medialibrary/models.py:261 +#: module/medialibrary/models.py:264 msgid "description" msgstr "описание" -#: module/medialibrary/models.py:264 +#: module/medialibrary/models.py:267 msgid "media file translation" msgstr "перевод медиа-файла" -#: module/medialibrary/models.py:265 +#: module/medialibrary/models.py:268 msgid "media file translations" msgstr "переводы медиа-файлов" +#: module/page/extensions/excerpt.py:18 +msgid "excerpt" +msgstr "отрывок" + +#: module/page/extensions/excerpt.py:21 +msgid "Add a brief excerpt summarizing the content of this page." +msgstr "Добавьте краткий отрывок, резюмирующий содержание страницы." + +#: module/page/extensions/excerpt.py:25 +msgid "Excerpt" +msgstr "Отрывок" + +#: module/page/extensions/navigation.py:90 +#: module/page/extensions/navigation.py:152 +msgid "navigation extension" +msgstr "расширение навигации" + +#: module/page/extensions/navigation.py:156 +msgid "" +"Select the module providing subpages for this page if you need to customize " +"the navigation." +msgstr "" +"если вам нужно кастомизировать навигацию, выберите модуль, который снабдит " +"эту страницу подстраницами." + +#: module/page/extensions/navigation.py:179 +msgid "Navigation extension" +msgstr "Расширение навигации" + +#: module/page/extensions/navigationgroups.py:17 +msgid "Default" +msgstr "По умолчанию" + +#: module/page/extensions/navigationgroups.py:18 +msgid "Footer" +msgstr "Подвал" + +#: module/page/extensions/navigationgroups.py:25 +msgid "navigation group" +msgstr "навигационная группа" + +#: module/page/extensions/relatedpages.py:20 +msgid "Select pages that should be listed as related content." +msgstr "Выберите страницы, которые должны быть отображены как похожие." + +#: module/page/extensions/relatedpages.py:25 +msgid "Related pages" +msgstr "Похожие страницы" + +#: module/page/extensions/sites.py:21 +msgid "Site" +msgstr "Сайт" + +#: module/page/extensions/symlinks.py:23 +msgid "symlinked page" +msgstr "страница, привязанная символической ссылкой" + +#: module/page/extensions/symlinks.py:24 +msgid "All content is inherited from this page if given." +msgstr "Все содержимое наследуется с этой страницы (если указана)." + +#: module/page/extensions/titles.py:19 +msgid "content title" +msgstr "Заголовок содержимого" + +#: module/page/extensions/titles.py:22 +msgid "The first line is the main title, the following lines are subtitles." +msgstr "Первая строка — основной заголовок, следующие строки — подзаголовки." + +#: module/page/extensions/titles.py:26 +msgid "page title" +msgstr "заголовок страницы" + +#: module/page/extensions/titles.py:30 +msgid "" +"Page title for browser window. Same as title by default. Must be 69 " +"characters or fewer." +msgstr "" +"Заголовок страницы для окна браузера. По умолчанию = просто заголовку " +"страницы. Должен быть не длиннее 69 символов" + +#: module/page/extensions/titles.py:60 +msgid "Titles" +msgstr "Заголовки" + #: module/page/forms.py:186 msgid "This URL is already taken by an active page." msgstr "Этот URL уже занят активной страницей." @@ -588,237 +573,126 @@ msgstr "Этот URL уже занят другой активной стран msgid "This page does not allow attachment of child pages" msgstr "Эта страница не позволяет создание дочерних страниц" -#: module/page/modeladmins.py:42 +#: module/page/modeladmins.py:50 msgid "Other options" msgstr "Другие параметры" -#: module/page/modeladmins.py:80 module/page/models.py:182 +#: module/page/modeladmins.py:88 module/page/models.py:170 msgid "in navigation" msgstr "в навигации" -#: module/page/modeladmins.py:109 module/page/modeladmins.py:111 +#: module/page/modeladmins.py:117 module/page/modeladmins.py:119 msgid "Add child page" msgstr "Добавить дочернюю страницу" -#: module/page/modeladmins.py:120 module/page/modeladmins.py:122 -#: templates/admin/feincms/content_inline.html:9 +#: module/page/modeladmins.py:128 module/page/modeladmins.py:130 +#: templates/admin/feincms/content_inline.html:10 msgid "View on site" msgstr "Посмотреть на сайте" -#: module/page/modeladmins.py:142 +#: module/page/modeladmins.py:150 #, python-format msgid "Add %(language)s translation of \"%(page)s\"" msgstr "Добавить %(language)s перевод \"%(page)s\"" -#: module/page/modeladmins.py:177 +#: module/page/modeladmins.py:185 msgid "" "The content from the original translation has been copied to the newly " "created page." msgstr "" -"Содержание на языке по-умолчанию было скопировано в свежесозданную " -"страницу." -#: module/page/modeladmins.py:197 +"Содержание на языке по умолчанию было скопировано в свежесозданную страницу." + +#: module/page/modeladmins.py:201 msgid "You don't have the necessary permissions to edit this object" msgstr "Вы не имеете необходимые права на изменение этого объекта" -#: module/page/modeladmins.py:223 +#: module/page/modeladmins.py:228 msgid "inherited" msgstr "унаследованный" -#: module/page/modeladmins.py:229 +#: module/page/modeladmins.py:234 msgid "extensions" msgstr "расширения" -#: module/page/modeladmins.py:233 module/page/models.py:221 +#: module/page/modeladmins.py:237 module/page/models.py:213 msgid "is active" msgstr "активная?" -#: module/page/models.py:169 +#: module/page/models.py:156 msgid "active" msgstr "активная" -#: module/page/models.py:173 -#, fuzzy +#: module/page/models.py:160 msgid "This title is also used for navigation menu items." msgstr "Этот заголовок также используется в навигации." -#: module/page/models.py:176 +#: module/page/models.py:163 msgid "This is used to build the URL for this page" msgstr "Это используется для создания URL этой страницы" -#: module/page/models.py:184 +#: module/page/models.py:172 msgid "override URL" msgstr "переопределение URL" -#: module/page/models.py:186 +#: module/page/models.py:174 msgid "" "Override the target URL. Be sure to include slashes at the beginning and at " "the end if it is a local URL. This affects both the navigation and subpages' " "URLs." msgstr "" -"Переопределение целевой URL. Не забудьте добавить слэш в начале и в " -"конце, если это локальный URL. Это влияет на навигацию и URL дочерних страниц." -#: module/page/models.py:190 +"Переопределение целевой URL. Не забудьте добавить слэш в начале и в конце, " +"если это локальный URL. Это влияет на навигацию и URL дочерних страниц." + +#: module/page/models.py:178 msgid "redirect to" msgstr "редирект на" -#: module/page/models.py:193 +#: module/page/models.py:181 msgid "Target URL for automatic redirects or the primary key of a page." -msgstr "Целевой URL для автоматических переадресации или первичный ключ страницы." +msgstr "" +"Целевой URL для автоматической переадресации или первичный ключ страницы." -#: module/page/models.py:196 +#: module/page/models.py:184 msgid "Cached URL" msgstr "Кешированный URL" -#: module/page/models.py:298 +#: module/page/models.py:286 #, python-format msgid "" "This %(page_class)s uses a singleton template, and " "FEINCMS_SINGLETON_TEMPLATE_DELETION_ALLOWED=False" msgstr "" +"Этот %(page_class)s использует шаблон-синглтон, и " +"FEINCMS_SINGLETON_TEMPLATE_DELETION_ALLOWED=False" -#: module/page/models.py:426 +#: module/page/models.py:372 msgid "page" msgstr "страница" -#: module/page/models.py:427 +#: module/page/models.py:373 msgid "pages" msgstr "страницы" -#: module/page/extensions/excerpt.py:18 -msgid "excerpt" -msgstr "отрывок" - -#: module/page/extensions/excerpt.py:21 -msgid "Add a brief excerpt summarizing the content of this page." -msgstr "Добавьте краткий отрывок резюмируя содержание" - -#: module/page/extensions/excerpt.py:25 -msgid "Excerpt" -msgstr "Отрывок" - -#: module/page/extensions/navigation.py:86 -#: module/page/extensions/navigation.py:115 -msgid "navigation extension" -msgstr "расширение навигации" - -#: module/page/extensions/navigation.py:119 -msgid "" -"Select the module providing subpages for this page if you need to customize " -"the navigation." -msgstr "" -"Выберите модуль который обеспечит подстраницы для этой страницы, если вам нужно настроить" -"навигацию." -#: module/page/extensions/navigation.py:134 -msgid "Navigation extension" -msgstr "Расширение навигации" - -#: module/page/extensions/relatedpages.py:21 -msgid "Select pages that should be listed as related content." -msgstr "Выберите страницы, которые должны быть отображены как похожие." - -#: module/page/extensions/relatedpages.py:26 -msgid "Related pages" -msgstr "Похожие страницы" - -#: module/page/extensions/sites.py:21 -msgid "Site" -msgstr "Сайт" - -#: module/page/extensions/symlinks.py:22 -msgid "symlinked page" -msgstr "страница привязанная символической ссылкой" - -#: module/page/extensions/symlinks.py:23 -msgid "All content is inherited from this page if given." -msgstr "Все содержимое наследуется с этой страницы." - -#: module/page/extensions/titles.py:19 -msgid "content title" -msgstr "Заголовок содержимого" - -#: module/page/extensions/titles.py:22 -msgid "The first line is the main title, the following lines are subtitles." -msgstr "Первая строка — основной заголовок, следующие строки — подзаголовки." - -#: module/page/extensions/titles.py:26 -msgid "page title" -msgstr "заголовок страницы" - -#: module/page/extensions/titles.py:30 -#, fuzzy -msgid "" -"Page title for browser window. Same as title bydefault. Must not be longer " -"than 70 characters." -msgstr "" -"Заголовок страницы для окна браузера. По умолчанию = просто заголовку " -"страницы." - -#: module/page/extensions/titles.py:60 -msgid "Titles" -msgstr "Заголовки" - -#: templates/admin/filter.html:3 -#, python-format -msgid " By %(filter_title)s " -msgstr " По %(filter_title)s " - -#: templates/admin/feincms/_messages_js.html:4 +#: templates/admin/feincms/_messages_js.html:5 msgid "Really delete item?" msgstr "Вы уверены, что хотите удалить элемент?" -#: templates/admin/feincms/_messages_js.html:4 -msgid "Confirm to delete item" -msgstr "Подтвердите удаление элемента" - -#: templates/admin/feincms/_messages_js.html:5 -msgid "Item deleted successfully." -msgstr "Элемент успешно удален." - -#: templates/admin/feincms/_messages_js.html:5 -msgid "Cannot delete item" -msgstr "Не выходит удалить элемент" - #: templates/admin/feincms/_messages_js.html:6 -msgid "Cannot delete item, because it is parent of at least one other item." -msgstr "" -"Не выходит удалить элемент, т.к. он является родителем как минимум для " -"одного другого элемента." +msgid "Really change template? All changes are saved." +msgstr "Точно сменить шаблон? Все изменения сохранены." #: templates/admin/feincms/_messages_js.html:7 -msgid "Change template" -msgstr "Изменить шаблон" - -#: templates/admin/feincms/_messages_js.html:8 -msgid "Really change template?
    All changes are saved." -msgstr "Точно сменить шаблон?
    Все изменения сохранены." - -#: templates/admin/feincms/_messages_js.html:9 #, python-format msgid "" -"Really change template?
    All changes are saved and content from " -"%%(source_regions)s is moved to %%(target_region)s." +"Really change template? All changes are saved and content from " +"%%(source_regions)s is moved to %%(target_region)s." msgstr "" +"Действительно изменить шаблон? Все изменения сохранены и контент из " +"%%(source_regions)s перенесён в %%(target_region)s." -#: templates/admin/feincms/_messages_js.html:12 -msgid "Hide" -msgstr "Скрыть" - -#: templates/admin/feincms/_messages_js.html:13 -msgid "Show" -msgstr "Покзать" - -#: templates/admin/feincms/_messages_js.html:14 -msgid "After" -msgstr "После" - -#: templates/admin/feincms/_messages_js.html:15 -msgid "Before" -msgstr "До" - -#: templates/admin/feincms/_messages_js.html:16 -msgid "Insert new:" -msgstr "Вставить перед этим элементом" +#: templates/admin/feincms/_messages_js.html:8 +msgid "Move to region:" +msgstr "Перенести в регион:" #: templates/admin/feincms/content_editor.html:15 msgid "Copy content from the original" @@ -833,101 +707,83 @@ msgid "" "Content from the parent site is automatically inherited. To override this " "behaviour, add some content." msgstr "" -"Контент автоматически унаследован от родительского узла. Чтобы переопределить это" -"поведение, добавьте содержимое." -#: templates/admin/feincms/content_editor.html:33 -msgid "Add new item" -msgstr "Добавить элемент" +"Контент автоматически унаследован от родительского узла. Чтобы " +"переопределить это поведение, добавьте содержимое." -#: templates/admin/feincms/content_inline.html:93 -#, python-format -msgid "Add another %(verbose_name)s" -msgstr "Добавить еще %(verbose_name)s" +#: templates/admin/feincms/content_inline.html:8 +msgid "Change" +msgstr "Изменить" -#: templates/admin/feincms/content_inline.html:96 +#: templates/admin/feincms/content_inline.html:27 msgid "Remove" msgstr "Удалить" -#: templates/admin/feincms/fe_editor.html:30 -msgid "Save" -msgstr "Сохранить" - -#: templates/admin/feincms/fe_tools.html:28 -msgid "Stop Editing" -msgstr "Закончить редактирование" - -#: templates/admin/feincms/fe_tools.html:33 -msgid "edit" -msgstr "редактировать" - -#: templates/admin/feincms/fe_tools.html:35 -msgid "new" -msgstr "новый" - -#: templates/admin/feincms/fe_tools.html:36 -msgid "up" -msgstr "вверх" +#: templates/admin/feincms/content_inline.html:28 +#, python-format +msgid "Add another %(verbose_name)s" +msgstr "Добавить еще %(verbose_name)s" -#: templates/admin/feincms/fe_tools.html:37 -msgid "down" -msgstr "вниз" +#: templates/admin/feincms/content_type_selection_widget.html:3 +msgid "Insert new content:" +msgstr "Вставить новый контент:" -#: templates/admin/feincms/fe_tools.html:38 -msgid "remove" -msgstr "удалить" +#: templates/admin/feincms/item_editor.html:32 +msgid "available translations" +msgstr "доступные переводы" -#: templates/admin/feincms/recover_form.html:8 -#: templates/admin/feincms/revision_form.html:8 -#: templates/admin/feincms/page/page/item_editor.html:23 +#: templates/admin/feincms/page/page/item_editor.html:19 #: templates/admin/feincms/page/page/tree_editor.html:7 +#: templates/admin/feincms/recover_form.html:7 +#: templates/admin/feincms/revision_form.html:7 msgid "Home" msgstr "Начало" -#: templates/admin/feincms/recover_form.html:11 +#: templates/admin/feincms/page/page/item_editor.html:23 +msgid "Add" +msgstr "Добавить" + +#: templates/admin/feincms/recover_form.html:10 #, python-format msgid "Recover deleted %(verbose_name)s" msgstr "Восстановить удаленные %(verbose_name)s" -#: templates/admin/feincms/recover_form.html:17 +#: templates/admin/feincms/recover_form.html:16 msgid "Press the save button below to recover this version of the object." -msgstr "Нажмите кнопку Сохранить ниже, чтобы восстановить эту версию объекта." +msgstr "Нажмите кнопку \"Сохранить\" ниже, чтобы восстановить эту версию объекта." -#: templates/admin/feincms/revision_form.html:12 +#: templates/admin/feincms/revision_form.html:11 msgid "History" msgstr "История" -#: templates/admin/feincms/revision_form.html:13 +#: templates/admin/feincms/revision_form.html:12 #, python-format msgid "Revert %(verbose_name)s" msgstr "Восстановить %(verbose_name)s" -#: templates/admin/feincms/revision_form.html:24 +#: templates/admin/feincms/revision_form.html:23 msgid "Press the save button below to revert to this version of the object." -msgstr "Нажмите кнопку Сохранить ниже, чтобы восстановить эту версию объекта." +msgstr "Нажмите кнопку \"Сохранить\" ниже, чтобы восстановить эту версию объекта." -#: templates/admin/feincms/tree_editor.html:22 +#: templates/admin/feincms/tree_editor.html:21 msgid "Shortcuts" msgstr "Управление деревом" -#: templates/admin/feincms/tree_editor.html:24 +#: templates/admin/feincms/tree_editor.html:23 msgid "Collapse tree" msgstr "Свернуть" -#: templates/admin/feincms/tree_editor.html:25 +#: templates/admin/feincms/tree_editor.html:24 msgid "Expand tree" msgstr "Развернуть" -#: templates/admin/feincms/tree_editor.html:29 +#: templates/admin/feincms/tree_editor.html:28 msgid "Filter" msgstr "Фильтр" -#: templates/admin/feincms/page/page/item_editor.html:10 -msgid "Edit on site" -msgstr "Редактировать на сайте" - -#: templates/admin/feincms/page/page/item_editor.html:27 -msgid "Add" -msgstr "Добавить" +#: templates/admin/filter.html:3 +#, python-format +msgid " By %(filter_title)s " +msgstr " По %(filter_title)s " #: templates/admin/medialibrary/add_to_category.html:5 #: templates/admin/medialibrary/add_to_category.html:9 @@ -950,44 +806,18 @@ msgstr "Добавить в категорию" msgid "Cancel" msgstr "Отмена" -#: templates/admin/medialibrary/mediafile/change_list.html:11 +#: templates/admin/medialibrary/mediafile/change_list.html:10 msgid "Bulk upload a ZIP file:" msgstr "Загрузить несколько файлов в ZIP-архиве:" -#: templates/admin/medialibrary/mediafile/change_list.html:21 +#: templates/admin/medialibrary/mediafile/change_list.html:20 msgid "Overwrite" msgstr "Перезаписать" -#: templates/admin/medialibrary/mediafile/change_list.html:24 +#: templates/admin/medialibrary/mediafile/change_list.html:23 msgid "Send" msgstr "Отправить" -#: templates/content/comments/default.html:10 -#, python-format -msgid "%(comment_count)s comments." -msgstr "%(comment_count)s комментариев." - -#: templates/content/comments/default.html:18 -#, python-format -msgid "" -"\n" -" %(comment_username)s said on %(comment_submit_date)s
    \n" -" " -msgstr "" -"\n" -" Пользователь %(comment_username)s сказал " -"(%(comment_submit_date)s)
    \n" -" " - -#: templates/content/comments/default.html:28 -msgid "No comments." -msgstr "Комментариев нет." - -#: templates/content/comments/default.html:36 -msgid "Post Comment" -msgstr "Отправить комментарий" - #: templates/content/contactform/form.html:9 msgid "Submit" msgstr "Отправить" @@ -996,6 +826,159 @@ msgstr "Отправить" msgid "Thanks!" msgstr "Спасибо!" +#~ msgid "enabled" +#~ msgstr "включены" + +#~ msgid "New comments may be added" +#~ msgstr "Новые комментарии могут быть добавлены" + +#~ msgid "comments" +#~ msgstr "комментарии" + +#~ msgid "public" +#~ msgstr "Опубликовано" + +#~ msgid "not public" +#~ msgstr "Не опубликовано" + +#~ msgid "HTML Tidy" +#~ msgstr "HTML Tidy" + +#~ msgid "Ignore the HTML validation warnings" +#~ msgstr "Игнорировать предупреждения при валидации HTML" + +#~ msgid "" +#~ "HTML validation produced %(count)d warnings. Please review the updated " +#~ "content below before continuing: %(messages)s" +#~ msgstr "" +#~ "HTML валидация вызвала %(count)d предупреждений. Пожалуйста просмотрите " +#~ "ниже обновленное содержимое прежде чем продолжить: %(messages)s" + +#~ msgid "" +#~ "The rss field is updated several times a day. A change in the title will " +#~ "only be visible on the home page after the next feed update." +#~ msgstr "" +#~ "RSS поле обновляется несколько раз в день. Изменение в названии будет " +#~ "видно на главной странице только после следующего обновления фида" + +#~ msgid "link" +#~ msgstr "ссылка" + +#~ msgid "pre-rendered content" +#~ msgstr "предподготовленное содержимое" + +#~ msgid "last updated" +#~ msgstr "последнее обновление" + +#~ msgid "max. items" +#~ msgstr "макс. число элементов" + +#~ msgid "RSS feed" +#~ msgstr "RSS фид" + +#~ msgid "RSS feeds" +#~ msgstr "RSS фиды" + +#~ msgid "tags" +#~ msgstr "теги" + +#~ msgid "published" +#~ msgstr "опубликовано" + +#~ msgid "This is used for the generated navigation too." +#~ msgstr "Используется также в сгенерированно навигации." + +#~ msgid "published on" +#~ msgstr "опубликовано" + +#~ msgid "" +#~ "Will be set automatically once you tick the `published` checkbox above." +#~ msgstr "" +#~ "Будет установлено автоматически, как только Вы отметите пункт " +#~ "\"опубликовано\" выше." + +#~ msgid "entry" +#~ msgstr "запись" + +#~ msgid "entries" +#~ msgstr "записи" + +#~ msgid "Confirm to delete item" +#~ msgstr "Подтвердите удаление элемента" + +#~ msgid "Item deleted successfully." +#~ msgstr "Элемент успешно удален." + +#~ msgid "Cannot delete item" +#~ msgstr "Не выходит удалить элемент" + +#~ msgid "Cannot delete item, because it is parent of at least one other item." +#~ msgstr "" +#~ "Не выходит удалить элемент, т.к. он является родителем как минимум для " +#~ "одного другого элемента." + +#~ msgid "Change template" +#~ msgstr "Изменить шаблон" + +#~ msgid "Hide" +#~ msgstr "Скрыть" + +#~ msgid "Show" +#~ msgstr "Покзать" + +#~ msgid "After" +#~ msgstr "После" + +#~ msgid "Before" +#~ msgstr "До" + +#~ msgid "Add new item" +#~ msgstr "Добавить элемент" + +#~ msgid "Save" +#~ msgstr "Сохранить" + +#~ msgid "Stop Editing" +#~ msgstr "Закончить редактирование" + +#~ msgid "edit" +#~ msgstr "редактировать" + +#~ msgid "new" +#~ msgstr "новый" + +#~ msgid "up" +#~ msgstr "вверх" + +#~ msgid "down" +#~ msgstr "вниз" + +#~ msgid "remove" +#~ msgstr "удалить" + +#~ msgid "Edit on site" +#~ msgstr "Редактировать на сайте" + +#~ msgid "%(comment_count)s comments." +#~ msgstr "%(comment_count)s комментариев." + +#~ msgid "" +#~ "\n" +#~ " %(comment_username)s said on " +#~ "%(comment_submit_date)s
    \n" +#~ " " +#~ msgstr "" +#~ "\n" +#~ " Пользователь %(comment_username)s сказал " +#~ "(%(comment_submit_date)s)
    \n" +#~ " " + +#~ msgid "No comments." +#~ msgstr "Комментариев нет." + +#~ msgid "Post Comment" +#~ msgstr "Отправить комментарий" + #~ msgid "table" #~ msgstr "таблица" diff --git a/feincms/locale/tr/LC_MESSAGES/django.po b/feincms/locale/tr/LC_MESSAGES/django.po index 52b458529..eb8a8e7c1 100644 --- a/feincms/locale/tr/LC_MESSAGES/django.po +++ b/feincms/locale/tr/LC_MESSAGES/django.po @@ -1,745 +1,782 @@ # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. -# +# # Translators: # byzgl , 2013 # byzgl , 2013 msgid "" msgstr "" "Project-Id-Version: FeinCMS\n" -"Report-Msgid-Bugs-To: http://github.com/feincms/feincms/issues\n" -"POT-Creation-Date: 2013-10-25 11:12+0200\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2014-07-20 14:59+0200\n" "PO-Revision-Date: 2013-10-25 09:15+0000\n" "Last-Translator: Matthias Kestenholz \n" -"Language-Team: Turkish (http://www.transifex.com/projects/p/feincms/language/tr/)\n" +"Language-Team: Turkish (http://www.transifex.com/projects/p/feincms/language/" +"tr/)\n" +"Language: tr\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Language: tr\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" -#: models.py:370 content/template/models.py:59 -msgid "template" -msgstr "tema" - -#: models.py:510 -msgid "ordering" -msgstr "sıralama" - -#: translations.py:259 module/blog/extensions/translations.py:17 -#: module/extensions/translations.py:123 -msgid "language" -msgstr "dil" - -#: admin/filterspecs.py:35 admin/filterspecs.py:70 +#: admin/filterspecs.py:47 admin/filterspecs.py:86 msgid "All" msgstr "Tümü" -#: admin/filterspecs.py:46 module/page/models.py:172 +#: admin/filterspecs.py:58 module/page/models.py:178 msgid "Parent" msgstr "Üst Öğe" -#: admin/filterspecs.py:81 +#: admin/filterspecs.py:97 #: templates/admin/medialibrary/mediafile/change_list.html:14 msgid "Category" msgstr "Kategori" -#: admin/item_editor.py:159 +#: admin/item_editor.py:190 #, python-format msgid "Change %s" msgstr "Değişiklik %s" -#: admin/tree_editor.py:234 content/rss/models.py:20 -#: content/section/models.py:34 module/blog/models.py:29 -#: module/medialibrary/models.py:40 module/page/models.py:170 -#: module/page/models.py:226 +#: admin/tree_editor.py:294 content/rss/models.py:23 +#: content/section/models.py:35 module/blog/models.py:35 +#: module/medialibrary/models.py:45 module/page/models.py:172 +#: module/page/models.py:240 msgid "title" msgstr "başlık" -#: admin/tree_editor.py:407 +#: admin/tree_editor.py:353 admin/tree_editor.py:370 +#, fuzzy +#| msgid "You don't have the necessary permissions to edit this object" +msgid "You do not have permission to modify this object" +msgstr "Bu nesneye düzenlemek için gerekli izinleriniz yok" + +#: admin/tree_editor.py:491 +msgid "No permission" +msgstr "" + +#: admin/tree_editor.py:509 #, python-format msgid "%s has been moved to a new position." msgstr "%s yeni bir pozisyona taşındı." -#: admin/tree_editor.py:411 +#: admin/tree_editor.py:512 msgid "Did not understand moving instruction." msgstr "Taşıma yönergeleri anlaşılamadı." -#: admin/tree_editor.py:420 +#: admin/tree_editor.py:523 msgid "actions" msgstr "olaylar" -#: admin/tree_editor.py:432 -#, python-format -msgid "Successfully deleted %s items." +#: admin/tree_editor.py:552 +#, fuzzy, python-format +#| msgid "Successfully deleted %s items." +msgid "Successfully deleted %(count)d items." msgstr "%s öğeleri başarıyla silindi." -#: admin/tree_editor.py:440 +#: admin/tree_editor.py:565 #, python-format msgid "Delete selected %(verbose_name_plural)s" msgstr "%(verbose_name_plural)s seçilenleri sil" -#: content/application/models.py:218 +#: content/application/models.py:147 msgid "application content" msgstr "uygulama içeriği" -#: content/application/models.py:219 +#: content/application/models.py:148 msgid "application contents" msgstr "uygulama içerikleri" -#: content/application/models.py:249 +#: content/application/models.py:179 msgid "application" msgstr "uygulama" -#: content/comments/models.py:24 +#: content/comments/models.py:32 msgid "enabled" msgstr "etkin" -#: content/comments/models.py:24 +#: content/comments/models.py:33 msgid "New comments may be added" msgstr "Yeni yorumlar eklenebilir" -#: content/comments/models.py:28 content/comments/models.py:29 +#: content/comments/models.py:37 content/comments/models.py:38 msgid "comments" msgstr "yorumlar" -#: content/comments/models.py:49 +#: content/comments/models.py:60 msgid "public" msgstr "genel" -#: content/comments/models.py:49 +#: content/comments/models.py:61 msgid "not public" msgstr "genel değil" -#: content/contactform/models.py:18 +#: content/contactform/models.py:20 msgid "name" msgstr "ad" -#: content/contactform/models.py:19 +#: content/contactform/models.py:21 msgid "email" msgstr "email" -#: content/contactform/models.py:20 +#: content/contactform/models.py:22 msgid "subject" msgstr "konu" -#: content/contactform/models.py:23 content/raw/models.py:14 +#: content/contactform/models.py:26 content/raw/models.py:16 msgid "content" msgstr "içerik" -#: content/contactform/models.py:34 +#: content/contactform/models.py:37 msgid "contact form" msgstr "iletişim formu" -#: content/contactform/models.py:35 +#: content/contactform/models.py:38 msgid "contact forms" msgstr "iletişim formları" -#: content/file/models.py:20 content/file/models.py:25 -#: module/medialibrary/models.py:84 +#: content/file/models.py:23 content/file/models.py:28 +#: module/medialibrary/models.py:94 msgid "file" msgstr "dosya" -#: content/file/models.py:26 +#: content/file/models.py:29 msgid "files" msgstr "dosyalar" -#: content/image/models.py:45 content/image/models.py:54 +#: content/image/models.py:46 content/image/models.py:55 msgid "image" msgstr "resim" -#: content/image/models.py:48 +#: content/image/models.py:49 msgid "alternate text" msgstr "alternatif yazı" -#: content/image/models.py:49 +#: content/image/models.py:50 msgid "Description of image" msgstr "Resim açıklaması" -#: content/image/models.py:50 module/medialibrary/models.py:231 +#: content/image/models.py:51 module/medialibrary/models.py:260 msgid "caption" msgstr "altyazı" -#: content/image/models.py:55 +#: content/image/models.py:56 msgid "images" msgstr "resimler" -#: content/image/models.py:80 +#: content/image/models.py:82 msgid "position" msgstr "pozisyon" -#: content/image/models.py:88 +#: content/image/models.py:90 msgid "format" msgstr "format" -#: content/medialibrary/models.py:49 content/section/models.py:36 -#: module/medialibrary/fields.py:59 module/medialibrary/models.py:97 +#: content/medialibrary/models.py:51 content/section/models.py:38 +#: module/medialibrary/fields.py:66 module/medialibrary/models.py:112 msgid "media file" msgstr "medya dosyası" -#: content/medialibrary/models.py:50 module/medialibrary/models.py:98 +#: content/medialibrary/models.py:52 module/medialibrary/models.py:113 msgid "media files" msgstr "medya dosyaları" -#: content/medialibrary/models.py:58 content/section/models.py:52 -#: content/table/models.py:82 +#: content/medialibrary/models.py:64 content/section/models.py:59 msgid "type" msgstr "tür" -#: content/raw/models.py:18 +#: content/raw/models.py:20 msgid "raw content" msgstr "ham içerik" -#: content/raw/models.py:19 +#: content/raw/models.py:21 msgid "raw contents" msgstr "ham içerikler" -#: content/richtext/models.py:22 +#: content/richtext/models.py:24 msgid "HTML Tidy" msgstr "HTML Toparla" -#: content/richtext/models.py:23 +#: content/richtext/models.py:25 msgid "Ignore the HTML validation warnings" msgstr "HTML doğrulama uyarılarını yoksay" -#: content/richtext/models.py:47 +#: content/richtext/models.py:55 #, python-format msgid "" "HTML validation produced %(count)d warnings. Please review the updated " "content below before continuing: %(messages)s" -msgstr "HTML doğrulama işlemi %(count)d uyarı. Devam etmeden önce aşağıdaki güncellenen içeriği gözden geçirin: %(messages)s" +msgstr "" +"HTML doğrulama işlemi %(count)d uyarı. Devam etmeden önce aşağıdaki " +"güncellenen içeriği gözden geçirin: %(messages)s" -#: content/richtext/models.py:84 content/section/models.py:35 +#: content/richtext/models.py:99 content/section/models.py:36 msgid "text" msgstr "yazı" -#: content/richtext/models.py:88 +#: content/richtext/models.py:103 msgid "rich text" msgstr "zengin yazı" -#: content/richtext/models.py:89 +#: content/richtext/models.py:104 msgid "rich texts" msgstr "zengin yazı" -#: content/rss/models.py:21 +#: content/rss/models.py:25 msgid "" "The rss field is updated several times a day. A change in the title will " "only be visible on the home page after the next feed update." -msgstr "Rss alanı günde birkaç kez güncellenir. Başlıktaki bir değişiklik sadece bir sonraki besleme güncellemesinden sonra ana sayfasında görülebilir." +msgstr "" +"Rss alanı günde birkaç kez güncellenir. Başlıktaki bir değişiklik sadece bir " +"sonraki besleme güncellemesinden sonra ana sayfasında görülebilir." -#: content/rss/models.py:22 +#: content/rss/models.py:28 msgid "link" msgstr "link" -#: content/rss/models.py:23 +#: content/rss/models.py:30 msgid "pre-rendered content" msgstr "önden görüntülenen içerik" -#: content/rss/models.py:24 +#: content/rss/models.py:32 msgid "last updated" msgstr "son güncelleme" -#: content/rss/models.py:25 +#: content/rss/models.py:33 msgid "max. items" msgstr "max. öğe" -#: content/rss/models.py:29 +#: content/rss/models.py:37 msgid "RSS feed" msgstr "RSS besleme" -#: content/rss/models.py:30 +#: content/rss/models.py:38 msgid "RSS feeds" msgstr "RSS beslemeler" -#: content/section/models.py:41 +#: content/section/models.py:43 msgid "section" msgstr "bölüm" -#: content/section/models.py:42 +#: content/section/models.py:44 msgid "sections" msgstr "bölümler" -#: content/table/models.py:63 -msgid "plain" -msgstr "düz" - -#: content/table/models.py:64 -msgid "title row" -msgstr "başlık satırı" - -#: content/table/models.py:66 -msgid "title row and column" -msgstr "başlık satır ve sütunu" - -#: content/table/models.py:72 -msgid "table" -msgstr "tablo" - -#: content/table/models.py:73 -msgid "tables" -msgstr "tablolar" - -#: content/table/models.py:87 -msgid "data" -msgstr "veri" - -#: content/template/models.py:51 +#: content/template/models.py:53 msgid "template content" msgstr "tema içeriği" -#: content/template/models.py:52 +#: content/template/models.py:54 msgid "template contents" msgstr "tema içerikleri" -#: content/video/models.py:31 +#: content/template/models.py:63 models.py:400 +msgid "template" +msgstr "tema" + +#: content/video/models.py:34 msgid "video link" msgstr "video linki" -#: content/video/models.py:32 +#: content/video/models.py:36 msgid "" -"This should be a link to a youtube or vimeo video, i.e.: " -"http://www.youtube.com/watch?v=zmj1rpzDRZ0" -msgstr "Bu, bir youtube veya vimeo video bir bağlantı olmalıdır, ör: http://www.youtube.com/watch?v=zmj1rpzDRZ0" +"This should be a link to a youtube or vimeo video, i.e.: http://www.youtube." +"com/watch?v=zmj1rpzDRZ0" +msgstr "" +"Bu, bir youtube veya vimeo video bir bağlantı olmalıdır, ör: http://www." +"youtube.com/watch?v=zmj1rpzDRZ0" -#: content/video/models.py:37 +#: content/video/models.py:41 msgid "video" msgstr "video" -#: content/video/models.py:38 +#: content/video/models.py:42 msgid "videos" msgstr "vidyolar" -#: contrib/tagging.py:117 +#: contrib/tagging.py:132 msgid "Tagging" msgstr "Etiketleme" -#: module/blog/models.py:28 +#: models.py:550 +msgid "ordering" +msgstr "sıralama" + +#: module/blog/extensions/tags.py:17 +msgid "tags" +msgstr "etiketler" + +#: module/blog/extensions/translations.py:25 +#: module/extensions/translations.py:140 translations.py:282 +msgid "language" +msgstr "dil" + +#: module/blog/extensions/translations.py:35 +#: module/extensions/translations.py:148 +msgid "translation of" +msgstr "tercümesi" + +#: module/blog/extensions/translations.py:39 +#: module/extensions/translations.py:152 +msgid "Leave this empty for entries in the primary language." +msgstr "Ana dilde girişler için boş bırakın." + +#: module/blog/extensions/translations.py:67 +#: templates/admin/feincms/item_editor.html:33 +msgid "available translations" +msgstr "kullanılabilir çeviriler" + +#: module/blog/models.py:33 msgid "published" msgstr "yayınlandı" -#: module/blog/models.py:30 +#: module/blog/models.py:36 msgid "This is used for the generated navigation too." msgstr "Bu da oluşturulan navigasyon için kullanılır." -#: module/blog/models.py:33 +#: module/blog/models.py:40 msgid "published on" msgstr "yayınlanan tarih" -#: module/blog/models.py:34 -msgid "" -"Will be set automatically once you tick the `published` checkbox above." -msgstr "Yukarıdaki `yayınlanan` onay kutusunu bir kez işaretleyin otomatik olarak ayarlanır." +#: module/blog/models.py:42 +msgid "Will be set automatically once you tick the `published` checkbox above." +msgstr "" +"Yukarıdaki `yayınlanan` onay kutusunu bir kez işaretleyin otomatik olarak " +"ayarlanır." -#: module/blog/models.py:39 +#: module/blog/models.py:48 msgid "entry" msgstr "giriş" -#: module/blog/models.py:40 +#: module/blog/models.py:49 msgid "entries" msgstr "girişler" -#: module/blog/extensions/tags.py:12 -msgid "tags" -msgstr "etiketler" - -#: module/blog/extensions/translations.py:20 -#: module/extensions/translations.py:126 -msgid "translation of" -msgstr "tercümesi" - -#: module/blog/extensions/translations.py:23 -#: module/extensions/translations.py:129 -msgid "Leave this empty for entries in the primary language." -msgstr "Ana dilde girişler için boş bırakın." - -#: module/blog/extensions/translations.py:44 -#: templates/admin/feincms/item_editor.html:43 -msgid "available translations" -msgstr "kullanılabilir çeviriler" - -#: module/extensions/changedate.py:32 +#: module/extensions/changedate.py:41 msgid "creation date" msgstr "oluşturulma tarihi" -#: module/extensions/changedate.py:33 +#: module/extensions/changedate.py:43 msgid "modification date" msgstr "düzenlenme tarihi" -#: module/extensions/ct_tracker.py:140 +#: module/extensions/ct_tracker.py:156 msgid "content types" msgstr "içerik tipleri" -#: module/extensions/datepublisher.py:67 +#: module/extensions/datepublisher.py:86 msgid "publication date" msgstr "yayın tarihi" -#: module/extensions/datepublisher.py:70 +#: module/extensions/datepublisher.py:90 msgid "publication end date" msgstr "yayın bitiş tarihi" -#: module/extensions/datepublisher.py:72 +#: module/extensions/datepublisher.py:93 msgid "Leave empty if the entry should stay active forever." msgstr "Giriş sonsuza kadar aktif kalmalı ise boş bırakın." -#: module/extensions/datepublisher.py:103 +#: module/extensions/datepublisher.py:128 msgid "visible from - to" msgstr "için - görünür " -#: module/extensions/datepublisher.py:113 +#: module/extensions/datepublisher.py:139 msgid "Date-based publishing" msgstr "Tarih-bazlı yayınlama" -#: module/extensions/featured.py:9 +#: module/extensions/featured.py:15 msgid "featured" msgstr "öne çıkan" -#: module/extensions/featured.py:14 +#: module/extensions/featured.py:21 msgid "Featured" msgstr "Öne çıkan" -#: module/extensions/seo.py:9 +#: module/extensions/seo.py:16 msgid "meta keywords" msgstr "meta anahtar kelimeler" -#: module/extensions/seo.py:10 -msgid "This will be prepended to the default keyword list." -msgstr "Bu, varsayılan anahtar kelime listesine başına eklenecektir." +#: module/extensions/seo.py:18 +msgid "Keywords are ignored by most search engines." +msgstr "" -#: module/extensions/seo.py:11 +#: module/extensions/seo.py:20 msgid "meta description" msgstr "meta açıklaması" -#: module/extensions/seo.py:12 -msgid "This will be prepended to the default description." -msgstr "Bu varsayılan açıklamanın başına eklenecektir." +#: module/extensions/seo.py:22 +msgid "" +"This text is displayed on the search results page. It is however not used " +"for the SEO ranking. Text longer than 140 characters is truncated." +msgstr "" -#: module/extensions/seo.py:17 +#: module/extensions/seo.py:32 msgid "Search engine optimization" msgstr "Arama motoru optimizasyonu" -#: module/extensions/translations.py:207 +#: module/extensions/translations.py:249 msgid "Edit translation" msgstr "Çeviriyi düzemle" -#: module/extensions/translations.py:210 +#: module/extensions/translations.py:256 msgid "Create translation" msgstr "Çeviri oluştur" -#: module/extensions/translations.py:215 +#: module/extensions/translations.py:264 msgid "translations" msgstr "çeviriler" -#: module/medialibrary/forms.py:26 +#: module/medialibrary/forms.py:31 msgid "This would create a loop in the hierarchy" msgstr "Bu hiyerarşi içinde bir döngü yaratacak" -#: module/medialibrary/forms.py:64 +#: module/medialibrary/forms.py:77 #, python-format msgid "" "Cannot overwrite with different file type (attempt to overwrite a " "%(old_ext)s with a %(new_ext)s)" -msgstr "Farklı dosya türü (%(old_ext)s ile bir %(new_ext)s üzerine yazma girişimi) ile üzerine yazamazsınız" +msgstr "" +"Farklı dosya türü (%(old_ext)s ile bir %(new_ext)s üzerine yazma girişimi) " +"ile üzerine yazamazsınız" -#: module/medialibrary/modeladmins.py:58 +#: module/medialibrary/modeladmins.py:63 #, python-format msgid "Successfully added %(count)d media file to %(category)s." msgid_plural "Successfully added %(count)d media files to %(category)s." msgstr[0] "%(count)d medya dosyaları %(category)s e başarıyla eklendi." msgstr[1] "%(count)d medya dosyaları %(category)s e başarıyla eklendi." -#: module/medialibrary/modeladmins.py:77 +#: module/medialibrary/modeladmins.py:84 msgid "Add selected media files to category" msgstr "Seçilen medya dosyalarını kategoriye ekle" -#: module/medialibrary/modeladmins.py:86 +#: module/medialibrary/modeladmins.py:94 #, python-format msgid "ZIP file exported as %s" msgstr "ZIP dosyası %s olarak dışa aktarıldı" -#: module/medialibrary/modeladmins.py:88 +#: module/medialibrary/modeladmins.py:96 #, python-format msgid "ZIP file export failed: %s" msgstr "ZIP dosyasını %s olarak dışa aktarmada hata" -#: module/medialibrary/modeladmins.py:93 +#: module/medialibrary/modeladmins.py:104 msgid "Export selected media files as zip file" msgstr "Seçilen medya dosyalarını ZIP dosyası olarak dışa aktar" -#: module/medialibrary/modeladmins.py:136 +#: module/medialibrary/modeladmins.py:157 #: templates/admin/feincms/page/page/item_editor.html:15 msgid "Preview" msgstr "Önizleme" -#: module/medialibrary/modeladmins.py:141 module/medialibrary/models.py:88 +#: module/medialibrary/modeladmins.py:162 module/medialibrary/models.py:103 msgid "file size" msgstr "dosya boyutu" -#: module/medialibrary/modeladmins.py:146 module/medialibrary/models.py:86 +#: module/medialibrary/modeladmins.py:167 module/medialibrary/models.py:100 msgid "created" msgstr "oluşturuldu" -#: module/medialibrary/modeladmins.py:165 module/medialibrary/models.py:85 +#: module/medialibrary/modeladmins.py:187 module/medialibrary/models.py:97 msgid "file type" msgstr "dosya tipi" -#: module/medialibrary/modeladmins.py:186 +#: module/medialibrary/modeladmins.py:211 msgid "file info" msgstr "dosya bilgisi" -#: module/medialibrary/modeladmins.py:198 +#: module/medialibrary/modeladmins.py:224 #, python-format msgid "%d files imported" msgstr "%d dosya içe aktarıldı" -#: module/medialibrary/modeladmins.py:200 +#: module/medialibrary/modeladmins.py:226 #, python-format msgid "ZIP import failed: %s" msgstr "ZIP dosyasını %s olarak içe aktarmada hata" -#: module/medialibrary/modeladmins.py:202 +#: module/medialibrary/modeladmins.py:228 msgid "No input file given" msgstr "Dosya girdisi yok" -#: module/medialibrary/models.py:43 +#: module/medialibrary/models.py:49 msgid "parent" msgstr "üst öğe" -#: module/medialibrary/models.py:45 module/page/models.py:171 +#: module/medialibrary/models.py:51 module/page/models.py:175 msgid "slug" msgstr "kurşun" -#: module/medialibrary/models.py:49 +#: module/medialibrary/models.py:55 msgid "category" msgstr "kategori" -#: module/medialibrary/models.py:50 module/medialibrary/models.py:90 +#: module/medialibrary/models.py:56 module/medialibrary/models.py:106 msgid "categories" msgstr "kategoriler" -#: module/medialibrary/models.py:87 +#: module/medialibrary/models.py:101 msgid "copyright" msgstr "telif hakkı" -#: module/medialibrary/models.py:202 +#: module/medialibrary/models.py:219 msgid "Image" msgstr "resim" -#: module/medialibrary/models.py:203 +#: module/medialibrary/models.py:221 msgid "Video" msgstr "Video" -#: module/medialibrary/models.py:204 +#: module/medialibrary/models.py:224 msgid "Audio" msgstr "Ses" -#: module/medialibrary/models.py:205 +#: module/medialibrary/models.py:226 msgid "PDF document" msgstr "PDF dökümanı" -#: module/medialibrary/models.py:206 +#: module/medialibrary/models.py:227 msgid "Flash" msgstr "Flash" -#: module/medialibrary/models.py:207 +#: module/medialibrary/models.py:228 msgid "Text" msgstr "Yazı" -#: module/medialibrary/models.py:208 +#: module/medialibrary/models.py:229 msgid "Rich Text" msgstr "Zengin Yazı" -#: module/medialibrary/models.py:209 +#: module/medialibrary/models.py:230 msgid "Zip archive" msgstr "Zip arşivi" -#: module/medialibrary/models.py:210 +#: module/medialibrary/models.py:231 msgid "Microsoft Word" msgstr "Microsoft Word" -#: module/medialibrary/models.py:211 +#: module/medialibrary/models.py:233 msgid "Microsoft Excel" msgstr "Microsoft Excel" -#: module/medialibrary/models.py:212 +#: module/medialibrary/models.py:235 msgid "Microsoft PowerPoint" msgstr "Microsoft PowerPoint" -#: module/medialibrary/models.py:213 +#: module/medialibrary/models.py:237 msgid "Binary" msgstr "Binary" -#: module/medialibrary/models.py:232 +#: module/medialibrary/models.py:261 msgid "description" msgstr "açıklama" -#: module/medialibrary/models.py:235 +#: module/medialibrary/models.py:264 msgid "media file translation" msgstr "medya dosyası çevirisi" -#: module/medialibrary/models.py:236 +#: module/medialibrary/models.py:265 msgid "media file translations" msgstr "medya dosyası çevirileri" -#: module/page/forms.py:172 +#: module/page/extensions/excerpt.py:18 +msgid "excerpt" +msgstr "alıntı" + +#: module/page/extensions/excerpt.py:21 +msgid "Add a brief excerpt summarizing the content of this page." +msgstr "Bu sayfanın içeriğini özetleyen kısa bir alıntı ekleyin." + +#: module/page/extensions/excerpt.py:25 +msgid "Excerpt" +msgstr "Alıntı" + +#: module/page/extensions/navigation.py:86 +#: module/page/extensions/navigation.py:115 +msgid "navigation extension" +msgstr "navigasyon bileşeni" + +#: module/page/extensions/navigation.py:119 +msgid "" +"Select the module providing subpages for this page if you need to customize " +"the navigation." +msgstr "" +"Navigasyonu özelleştirmeniz gerekiyorsa, bu altsayfaları sağlayan modülü " +"seçiniz." + +#: module/page/extensions/navigation.py:134 +msgid "Navigation extension" +msgstr "Navigasyon bileşeni" + +#: module/page/extensions/navigationgroups.py:20 +msgid "Default" +msgstr "" + +#: module/page/extensions/navigationgroups.py:21 +msgid "Footer" +msgstr "" + +#: module/page/extensions/navigationgroups.py:28 +#, fuzzy +#| msgid "in navigation" +msgid "navigation group" +msgstr "navigasyonda" + +#: module/page/extensions/relatedpages.py:21 +msgid "Select pages that should be listed as related content." +msgstr "İlişkili içerik olarak listelenecek sayfaları seçiniz" + +#: module/page/extensions/relatedpages.py:26 +msgid "Related pages" +msgstr "İlgili sayfalar" + +#: module/page/extensions/sites.py:21 +msgid "Site" +msgstr "Site" + +#: module/page/extensions/symlinks.py:22 +msgid "symlinked page" +msgstr "Sembolik olarak bağlanmış sayfa" + +#: module/page/extensions/symlinks.py:23 +msgid "All content is inherited from this page if given." +msgstr "Verilirse tüm içerik bu sayfadan devralınır." + +#: module/page/extensions/titles.py:19 +msgid "content title" +msgstr "içerik başlığı" + +#: module/page/extensions/titles.py:22 +msgid "The first line is the main title, the following lines are subtitles." +msgstr "İlk satır ana başlık, aşağıdaki satırlar ise alt başlıktır." + +#: module/page/extensions/titles.py:26 +msgid "page title" +msgstr "sayfa başlığı" + +#: module/page/extensions/titles.py:30 +#, fuzzy +#| msgid "Page title for browser window. Same as title by default." +msgid "" +"Page title for browser window. Same as title bydefault. Must not be longer " +"than 70 characters." +msgstr "" +"Tarayıcı penceresi için sayfa başlığı. Varsayılan olarak başlık ile aynı." + +#: module/page/extensions/titles.py:60 +msgid "Titles" +msgstr "Başlıklar" + +#: module/page/forms.py:187 msgid "This URL is already taken by an active page." msgstr "Bu URL aktif bir sayfa tarafından alınmış." -#: module/page/forms.py:190 +#: module/page/forms.py:206 msgid "This URL is already taken by another active page." msgstr "Bu URL zaten başka aktif bir sayfa tarafından alınmış." -#: module/page/modeladmins.py:41 +#: module/page/forms.py:211 +msgid "This page does not allow attachment of child pages" +msgstr "" + +#: module/page/modeladmins.py:42 msgid "Other options" msgstr "Diğer seçenekler" -#: module/page/modeladmins.py:90 module/page/models.py:174 +#: module/page/modeladmins.py:80 module/page/models.py:182 msgid "in navigation" msgstr "navigasyonda" -#: module/page/modeladmins.py:105 +#: module/page/modeladmins.py:109 module/page/modeladmins.py:111 msgid "Add child page" msgstr "Alt sayfa ekle" -#: module/page/modeladmins.py:107 +#: module/page/modeladmins.py:120 module/page/modeladmins.py:122 #: templates/admin/feincms/content_inline.html:9 msgid "View on site" msgstr "Sitede göster" -#: module/page/modeladmins.py:126 +#: module/page/modeladmins.py:142 #, python-format -msgid "Add %(language)s Translation of \"%(page)s\"" +msgid "Add %(language)s translation of \"%(page)s\"" msgstr "" -#: module/page/modeladmins.py:156 +#: module/page/modeladmins.py:177 msgid "" "The content from the original translation has been copied to the newly " "created page." msgstr "Orijinal çeviri içeriği yeni oluşturulan sayfaya kopyalandı." -#: module/page/modeladmins.py:170 +#: module/page/modeladmins.py:197 msgid "You don't have the necessary permissions to edit this object" msgstr "Bu nesneye düzenlemek için gerekli izinleriniz yok" -#: module/page/modeladmins.py:185 +#: module/page/modeladmins.py:223 msgid "inherited" msgstr "devralındı" -#: module/page/modeladmins.py:189 +#: module/page/modeladmins.py:229 msgid "extensions" msgstr "bileşenler" -#: module/page/modeladmins.py:193 module/page/models.py:206 +#: module/page/modeladmins.py:233 module/page/models.py:221 msgid "is active" msgstr "aktif" -#: module/page/models.py:167 +#: module/page/models.py:169 msgid "active" msgstr "aktif" -#: module/page/models.py:175 +#: module/page/models.py:173 +#, fuzzy +#| msgid "This is used for the generated navigation too." +msgid "This title is also used for navigation menu items." +msgstr "Bu da oluşturulan navigasyon için kullanılır." + +#: module/page/models.py:176 +msgid "This is used to build the URL for this page" +msgstr "" + +#: module/page/models.py:184 msgid "override URL" msgstr "URL geçersiz" -#: module/page/models.py:176 +#: module/page/models.py:186 msgid "" "Override the target URL. Be sure to include slashes at the beginning and at " -"the end if it is a local URL. This affects both the navigation and subpages'" -" URLs." -msgstr "Hedef URL geçersiz Yerel bir URL ise başında ve sonunda bölü eklemeyi unutmayın. Bu navigasyon ve alt sayfaları etkiler." +"the end if it is a local URL. This affects both the navigation and subpages' " +"URLs." +msgstr "" +"Hedef URL geçersiz Yerel bir URL ise başında ve sonunda bölü eklemeyi " +"unutmayın. Bu navigasyon ve alt sayfaları etkiler." -#: module/page/models.py:177 +#: module/page/models.py:190 msgid "redirect to" msgstr "buraya yönlendiriliyor" -#: module/page/models.py:178 +#: module/page/models.py:193 msgid "Target URL for automatic redirects or the primary key of a page." msgstr "" -#: module/page/models.py:180 +#: module/page/models.py:196 msgid "Cached URL" msgstr "Önbelleklenmiş URL" -#: module/page/models.py:395 +#: module/page/models.py:298 +#, python-format +msgid "" +"This %(page_class)s uses a singleton template, and " +"FEINCMS_SINGLETON_TEMPLATE_DELETION_ALLOWED=False" +msgstr "" + +#: module/page/models.py:426 msgid "page" msgstr "sayfa" -#: module/page/models.py:396 +#: module/page/models.py:427 msgid "pages" msgstr "sayfalar" -#: module/page/extensions/excerpt.py:9 -msgid "excerpt" -msgstr "alıntı" - -#: module/page/extensions/excerpt.py:10 -msgid "Add a brief excerpt summarizing the content of this page." -msgstr "Bu sayfanın içeriğini özetleyen kısa bir alıntı ekleyin." - -#: module/page/extensions/excerpt.py:12 -msgid "Excerpt" -msgstr "Alıntı" - -#: module/page/extensions/navigation.py:82 -#: module/page/extensions/navigation.py:107 -msgid "navigation extension" -msgstr "navigasyon bileşeni" - -#: module/page/extensions/navigation.py:110 -msgid "" -"Select the module providing subpages for this page if you need to customize " -"the navigation." -msgstr "Navigasyonu özelleştirmeniz gerekiyorsa, bu altsayfaları sağlayan modülü seçiniz." - -#: module/page/extensions/navigation.py:125 -msgid "Navigation extension" -msgstr "Navigasyon bileşeni" - -#: module/page/extensions/relatedpages.py:14 -msgid "Select pages that should be listed as related content." -msgstr "İlişkili içerik olarak listelenecek sayfaları seçiniz" - -#: module/page/extensions/relatedpages.py:19 -msgid "Related pages" -msgstr "İlgili sayfalar" - -#: module/page/extensions/sites.py:16 -msgid "Site" -msgstr "Site" - -#: module/page/extensions/symlinks.py:15 -msgid "symlinked page" -msgstr "Sembolik olarak bağlanmış sayfa" - -#: module/page/extensions/symlinks.py:16 -msgid "All content is inherited from this page if given." -msgstr "Verilirse tüm içerik bu sayfadan devralınır." - -#: module/page/extensions/titles.py:13 -msgid "content title" -msgstr "içerik başlığı" - -#: module/page/extensions/titles.py:14 -msgid "The first line is the main title, the following lines are subtitles." -msgstr "İlk satır ana başlık, aşağıdaki satırlar ise alt başlıktır." - -#: module/page/extensions/titles.py:15 -msgid "page title" -msgstr "sayfa başlığı" - -#: module/page/extensions/titles.py:16 -msgid "Page title for browser window. Same as title by default." -msgstr "Tarayıcı penceresi için sayfa başlığı. Varsayılan olarak başlık ile aynı." - -#: module/page/extensions/titles.py:43 -msgid "Titles" -msgstr "Başlıklar" - -#: templates/admin/filter.html:3 -#, python-format -msgid " By %(filter_title)s " -msgstr "%(filter_title)s ile" - #: templates/admin/feincms/_messages_js.html:4 msgid "Really delete item?" msgstr "Gerçekten öğe silisin mi?" @@ -771,10 +808,12 @@ msgstr "Tema değiştirilsin mi?
    Tüm değişiklikler kaydedildi." #: templates/admin/feincms/_messages_js.html:9 #, python-format msgid "" -"Really change template?
    All changes are saved and content from " -"%%(source_regions)s is moved to " -"%%(target_region)s." -msgstr "Gerçekten tema değişsin mi?
    Tüm değişiklikler kaydedildi ve %%(source_regions)s teki içerikler %%(target_region)s e taşındı." +"Really change template?
    All changes are saved and content from " +"%%(source_regions)s is moved to %%(target_region)s." +msgstr "" +"Gerçekten tema değişsin mi?
    Tüm değişiklikler kaydedildi ve " +"%%(source_regions)s teki içerikler %%(target_region)s e taşındı." #: templates/admin/feincms/_messages_js.html:12 msgid "Hide" @@ -808,22 +847,24 @@ msgstr "Bölge boş" msgid "" "Content from the parent site is automatically inherited. To override this " "behaviour, add some content." -msgstr "Üst siteden içerik otomatik devralınır. Bu davranışı geçersiz kılmak için, bazı içerikler ekleyin." +msgstr "" +"Üst siteden içerik otomatik devralınır. Bu davranışı geçersiz kılmak için, " +"bazı içerikler ekleyin." #: templates/admin/feincms/content_editor.html:33 msgid "Add new item" msgstr "Yeni öğe ekle" -#: templates/admin/feincms/content_inline.html:91 +#: templates/admin/feincms/content_inline.html:93 #, python-format msgid "Add another %(verbose_name)s" msgstr "Başka ekle %(verbose_name)s" -#: templates/admin/feincms/content_inline.html:94 +#: templates/admin/feincms/content_inline.html:96 msgid "Remove" msgstr "Kaldır" -#: templates/admin/feincms/fe_editor.html:40 +#: templates/admin/feincms/fe_editor.html:30 msgid "Save" msgstr "Kaydet" @@ -851,13 +892,21 @@ msgstr "aşağı" msgid "remove" msgstr "kaldır" -#: templates/admin/feincms/recover_form.html:8 -#: templates/admin/feincms/revision_form.html:8 +#: templates/admin/feincms/page/page/item_editor.html:10 +msgid "Edit on site" +msgstr "Sitede düzenle" + #: templates/admin/feincms/page/page/item_editor.html:23 #: templates/admin/feincms/page/page/tree_editor.html:7 +#: templates/admin/feincms/recover_form.html:8 +#: templates/admin/feincms/revision_form.html:8 msgid "Home" msgstr "Anasayfa" +#: templates/admin/feincms/page/page/item_editor.html:27 +msgid "Add" +msgstr "Ekle" + #: templates/admin/feincms/recover_form.html:11 #, python-format msgid "Recover deleted %(verbose_name)s" @@ -880,29 +929,26 @@ msgstr "Dön %(verbose_name)s" msgid "Press the save button below to revert to this version of the object." msgstr "Nesnenin bu sürümüne dönmek için aşağıdaki kaydet düğmesine basın." -#: templates/admin/feincms/tree_editor.html:32 +#: templates/admin/feincms/tree_editor.html:22 msgid "Shortcuts" msgstr "Kısayollar" -#: templates/admin/feincms/tree_editor.html:34 +#: templates/admin/feincms/tree_editor.html:24 msgid "Collapse tree" msgstr "Ağacı daralt" -#: templates/admin/feincms/tree_editor.html:35 +#: templates/admin/feincms/tree_editor.html:25 msgid "Expand tree" msgstr "Ağacı genişlet" -#: templates/admin/feincms/tree_editor.html:38 +#: templates/admin/feincms/tree_editor.html:29 msgid "Filter" msgstr "Filtre" -#: templates/admin/feincms/page/page/item_editor.html:10 -msgid "Edit on site" -msgstr "Sitede düzenle" - -#: templates/admin/feincms/page/page/item_editor.html:27 -msgid "Add" -msgstr "Ekle" +#: templates/admin/filter.html:3 +#, python-format +msgid " By %(filter_title)s " +msgstr "%(filter_title)s ile" #: templates/admin/medialibrary/add_to_category.html:5 #: templates/admin/medialibrary/add_to_category.html:9 @@ -946,9 +992,14 @@ msgstr "%(comment_count)s yorum" #, python-format msgid "" "\n" -" %(comment_username)s said on %(comment_submit_date)s
    \n" +" %(comment_username)s said on %(comment_submit_date)s
    \n" +" " +msgstr "" +"\n" +" %(comment_username)s dedi buna " +"%(comment_submit_date)s
    \n" " " -msgstr "\n %(comment_username)s dedi buna %(comment_submit_date)s
    \n " #: templates/content/comments/default.html:28 msgid "No comments." @@ -965,3 +1016,27 @@ msgstr "Gönder" #: templates/content/contactform/thanks.html:3 msgid "Thanks!" msgstr "Teşekkürler!" + +#~ msgid "plain" +#~ msgstr "düz" + +#~ msgid "title row" +#~ msgstr "başlık satırı" + +#~ msgid "title row and column" +#~ msgstr "başlık satır ve sütunu" + +#~ msgid "table" +#~ msgstr "tablo" + +#~ msgid "tables" +#~ msgstr "tablolar" + +#~ msgid "data" +#~ msgstr "veri" + +#~ msgid "This will be prepended to the default keyword list." +#~ msgstr "Bu, varsayılan anahtar kelime listesine başına eklenecektir." + +#~ msgid "This will be prepended to the default description." +#~ msgstr "Bu varsayılan açıklamanın başına eklenecektir." diff --git a/feincms/locale/zh_CN/LC_MESSAGES/django.po b/feincms/locale/zh_CN/LC_MESSAGES/django.po index d732c1914..7ad192d53 100644 --- a/feincms/locale/zh_CN/LC_MESSAGES/django.po +++ b/feincms/locale/zh_CN/LC_MESSAGES/django.po @@ -1,744 +1,772 @@ # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. -# +# # Translators: # aaffdd11 , 2011 # indexofire , 2011 msgid "" msgstr "" "Project-Id-Version: FeinCMS\n" -"Report-Msgid-Bugs-To: http://github.com/feincms/feincms/issues\n" -"POT-Creation-Date: 2013-10-25 11:12+0200\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2014-07-20 14:59+0200\n" "PO-Revision-Date: 2013-10-25 09:15+0000\n" "Last-Translator: Matthias Kestenholz \n" -"Language-Team: Chinese (China) (http://www.transifex.com/projects/p/feincms/language/zh_CN/)\n" +"Language-Team: Chinese (China) (http://www.transifex.com/projects/p/feincms/" +"language/zh_CN/)\n" +"Language: zh_CN\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Language: zh_CN\n" "Plural-Forms: nplurals=1; plural=0;\n" -#: models.py:370 content/template/models.py:59 -msgid "template" -msgstr "模板" - -#: models.py:510 -msgid "ordering" -msgstr "排序" - -#: translations.py:259 module/blog/extensions/translations.py:17 -#: module/extensions/translations.py:123 -msgid "language" -msgstr "语言" - -#: admin/filterspecs.py:35 admin/filterspecs.py:70 +#: admin/filterspecs.py:47 admin/filterspecs.py:86 msgid "All" msgstr "全部" -#: admin/filterspecs.py:46 module/page/models.py:172 +#: admin/filterspecs.py:58 module/page/models.py:178 msgid "Parent" msgstr "父级" -#: admin/filterspecs.py:81 +#: admin/filterspecs.py:97 #: templates/admin/medialibrary/mediafile/change_list.html:14 msgid "Category" msgstr "分类" -#: admin/item_editor.py:159 +#: admin/item_editor.py:190 #, python-format msgid "Change %s" msgstr "修改 %s" -#: admin/tree_editor.py:234 content/rss/models.py:20 -#: content/section/models.py:34 module/blog/models.py:29 -#: module/medialibrary/models.py:40 module/page/models.py:170 -#: module/page/models.py:226 +#: admin/tree_editor.py:294 content/rss/models.py:23 +#: content/section/models.py:35 module/blog/models.py:35 +#: module/medialibrary/models.py:45 module/page/models.py:172 +#: module/page/models.py:240 msgid "title" msgstr "标题" -#: admin/tree_editor.py:407 +#: admin/tree_editor.py:353 admin/tree_editor.py:370 +#, fuzzy +#| msgid "You don't have the necessary permissions to edit this object" +msgid "You do not have permission to modify this object" +msgstr "你无权编辑该对象" + +#: admin/tree_editor.py:491 +msgid "No permission" +msgstr "" + +#: admin/tree_editor.py:509 #, python-format msgid "%s has been moved to a new position." msgstr "%s 已被移至新的位置" -#: admin/tree_editor.py:411 +#: admin/tree_editor.py:512 msgid "Did not understand moving instruction." msgstr "无法接受的移动指令" -#: admin/tree_editor.py:420 +#: admin/tree_editor.py:523 msgid "actions" msgstr "动作" -#: admin/tree_editor.py:432 -#, python-format -msgid "Successfully deleted %s items." -msgstr "" +#: admin/tree_editor.py:552 +#, fuzzy, python-format +#| msgid "Successfully added %(count)d media file to %(category)s." +#| msgid_plural "Successfully added %(count)d media files to %(category)s." +msgid "Successfully deleted %(count)d items." +msgstr "Successfully added %(count)d media files to %(category)s." -#: admin/tree_editor.py:440 +#: admin/tree_editor.py:565 #, python-format msgid "Delete selected %(verbose_name_plural)s" msgstr "" -#: content/application/models.py:218 +#: content/application/models.py:147 msgid "application content" msgstr "应用程序内容" -#: content/application/models.py:219 +#: content/application/models.py:148 msgid "application contents" msgstr "应用程序内容" -#: content/application/models.py:249 +#: content/application/models.py:179 msgid "application" msgstr "应用程序" -#: content/comments/models.py:24 +#: content/comments/models.py:32 msgid "enabled" msgstr "激活" -#: content/comments/models.py:24 +#: content/comments/models.py:33 msgid "New comments may be added" msgstr "新的评论" -#: content/comments/models.py:28 content/comments/models.py:29 +#: content/comments/models.py:37 content/comments/models.py:38 msgid "comments" msgstr "评论" -#: content/comments/models.py:49 +#: content/comments/models.py:60 msgid "public" msgstr "公开" -#: content/comments/models.py:49 +#: content/comments/models.py:61 msgid "not public" msgstr "不公开" -#: content/contactform/models.py:18 +#: content/contactform/models.py:20 msgid "name" msgstr "名称" -#: content/contactform/models.py:19 +#: content/contactform/models.py:21 msgid "email" msgstr "电子邮件" -#: content/contactform/models.py:20 +#: content/contactform/models.py:22 msgid "subject" msgstr "主题" -#: content/contactform/models.py:23 content/raw/models.py:14 +#: content/contactform/models.py:26 content/raw/models.py:16 msgid "content" msgstr "内容" -#: content/contactform/models.py:34 +#: content/contactform/models.py:37 msgid "contact form" msgstr "联系表单" -#: content/contactform/models.py:35 +#: content/contactform/models.py:38 msgid "contact forms" msgstr "联系表单" -#: content/file/models.py:20 content/file/models.py:25 -#: module/medialibrary/models.py:84 +#: content/file/models.py:23 content/file/models.py:28 +#: module/medialibrary/models.py:94 msgid "file" msgstr "文件" -#: content/file/models.py:26 +#: content/file/models.py:29 msgid "files" msgstr "文件" -#: content/image/models.py:45 content/image/models.py:54 +#: content/image/models.py:46 content/image/models.py:55 msgid "image" msgstr "图片" -#: content/image/models.py:48 +#: content/image/models.py:49 msgid "alternate text" msgstr "" -#: content/image/models.py:49 +#: content/image/models.py:50 msgid "Description of image" msgstr "" -#: content/image/models.py:50 module/medialibrary/models.py:231 +#: content/image/models.py:51 module/medialibrary/models.py:260 msgid "caption" msgstr "题目" -#: content/image/models.py:55 +#: content/image/models.py:56 msgid "images" msgstr "图片" -#: content/image/models.py:80 +#: content/image/models.py:82 msgid "position" msgstr "位置" -#: content/image/models.py:88 +#: content/image/models.py:90 msgid "format" msgstr "" -#: content/medialibrary/models.py:49 content/section/models.py:36 -#: module/medialibrary/fields.py:59 module/medialibrary/models.py:97 +#: content/medialibrary/models.py:51 content/section/models.py:38 +#: module/medialibrary/fields.py:66 module/medialibrary/models.py:112 msgid "media file" msgstr "多媒体文件" -#: content/medialibrary/models.py:50 module/medialibrary/models.py:98 +#: content/medialibrary/models.py:52 module/medialibrary/models.py:113 msgid "media files" msgstr "多媒体文件" -#: content/medialibrary/models.py:58 content/section/models.py:52 -#: content/table/models.py:82 +#: content/medialibrary/models.py:64 content/section/models.py:59 msgid "type" msgstr "类型" -#: content/raw/models.py:18 +#: content/raw/models.py:20 msgid "raw content" msgstr "raw content" -#: content/raw/models.py:19 +#: content/raw/models.py:21 msgid "raw contents" msgstr "raw contents" -#: content/richtext/models.py:22 +#: content/richtext/models.py:24 msgid "HTML Tidy" msgstr "HTML Tidy" -#: content/richtext/models.py:23 +#: content/richtext/models.py:25 msgid "Ignore the HTML validation warnings" msgstr "忽略HTML验证错误警告" -#: content/richtext/models.py:47 +#: content/richtext/models.py:55 #, python-format msgid "" "HTML validation produced %(count)d warnings. Please review the updated " "content below before continuing: %(messages)s" -msgstr "HTML验证产生 %(count)d 个警告。请在继续前查看下面更新的内容: %(messages)s" +msgstr "" +"HTML验证产生 %(count)d 个警告。请在继续前查看下面更新的内容: %(messages)s" -#: content/richtext/models.py:84 content/section/models.py:35 +#: content/richtext/models.py:99 content/section/models.py:36 msgid "text" msgstr "纯文本" -#: content/richtext/models.py:88 +#: content/richtext/models.py:103 msgid "rich text" msgstr "富文本" -#: content/richtext/models.py:89 +#: content/richtext/models.py:104 msgid "rich texts" msgstr "富文本" -#: content/rss/models.py:21 +#: content/rss/models.py:25 msgid "" "The rss field is updated several times a day. A change in the title will " "only be visible on the home page after the next feed update." msgstr "RSS字段一天内更新多次。标题改动将在feed更新后只在主页显示。" -#: content/rss/models.py:22 +#: content/rss/models.py:28 msgid "link" msgstr "链接" -#: content/rss/models.py:23 +#: content/rss/models.py:30 msgid "pre-rendered content" msgstr "预渲染的内容" -#: content/rss/models.py:24 +#: content/rss/models.py:32 msgid "last updated" msgstr "最后更新" -#: content/rss/models.py:25 +#: content/rss/models.py:33 msgid "max. items" msgstr "最大条目数" -#: content/rss/models.py:29 +#: content/rss/models.py:37 msgid "RSS feed" msgstr "RSS 提要" -#: content/rss/models.py:30 +#: content/rss/models.py:38 msgid "RSS feeds" msgstr "RSS 提要" -#: content/section/models.py:41 +#: content/section/models.py:43 msgid "section" msgstr "版块" -#: content/section/models.py:42 +#: content/section/models.py:44 msgid "sections" msgstr "版块" -#: content/table/models.py:63 -msgid "plain" -msgstr "无格式" - -#: content/table/models.py:64 -msgid "title row" -msgstr "标题行" - -#: content/table/models.py:66 -msgid "title row and column" -msgstr "标题行和列" - -#: content/table/models.py:72 -msgid "table" -msgstr "表格" - -#: content/table/models.py:73 -msgid "tables" -msgstr "表格" - -#: content/table/models.py:87 -msgid "data" -msgstr "数据" - -#: content/template/models.py:51 +#: content/template/models.py:53 msgid "template content" msgstr "模板内容" -#: content/template/models.py:52 +#: content/template/models.py:54 msgid "template contents" msgstr "模板内容" -#: content/video/models.py:31 +#: content/template/models.py:63 models.py:400 +msgid "template" +msgstr "模板" + +#: content/video/models.py:34 msgid "video link" msgstr "视频链接" -#: content/video/models.py:32 +#: content/video/models.py:36 msgid "" -"This should be a link to a youtube or vimeo video, i.e.: " -"http://www.youtube.com/watch?v=zmj1rpzDRZ0" -msgstr "请输入youtube或vimeo视频链接,比如:http://www.youtube.com/watch?v=zmj1rpzDRZ0" +"This should be a link to a youtube or vimeo video, i.e.: http://www.youtube." +"com/watch?v=zmj1rpzDRZ0" +msgstr "" +"请输入youtube或vimeo视频链接,比如:http://www.youtube.com/watch?" +"v=zmj1rpzDRZ0" -#: content/video/models.py:37 +#: content/video/models.py:41 msgid "video" msgstr "视频" -#: content/video/models.py:38 +#: content/video/models.py:42 msgid "videos" msgstr "视频" -#: contrib/tagging.py:117 +#: contrib/tagging.py:132 msgid "Tagging" msgstr "" -#: module/blog/models.py:28 +#: models.py:550 +msgid "ordering" +msgstr "排序" + +#: module/blog/extensions/tags.py:17 +msgid "tags" +msgstr "标签" + +#: module/blog/extensions/translations.py:25 +#: module/extensions/translations.py:140 translations.py:282 +msgid "language" +msgstr "语言" + +#: module/blog/extensions/translations.py:35 +#: module/extensions/translations.py:148 +msgid "translation of" +msgstr "翻译" + +#: module/blog/extensions/translations.py:39 +#: module/extensions/translations.py:152 +msgid "Leave this empty for entries in the primary language." +msgstr "在主要语言项中对这些条目留空。" + +#: module/blog/extensions/translations.py:67 +#: templates/admin/feincms/item_editor.html:33 +msgid "available translations" +msgstr "已有翻译" + +#: module/blog/models.py:33 msgid "published" msgstr "已发布" -#: module/blog/models.py:30 +#: module/blog/models.py:36 msgid "This is used for the generated navigation too." msgstr "也被用于产生导航条" -#: module/blog/models.py:33 +#: module/blog/models.py:40 msgid "published on" msgstr "发布于" -#: module/blog/models.py:34 -msgid "" -"Will be set automatically once you tick the `published` checkbox above." +#: module/blog/models.py:42 +msgid "Will be set automatically once you tick the `published` checkbox above." msgstr "一旦你将`发表`复选框以上打勾选中,日志将设置成自动发布。" -#: module/blog/models.py:39 +#: module/blog/models.py:48 msgid "entry" msgstr "条目" -#: module/blog/models.py:40 +#: module/blog/models.py:49 msgid "entries" msgstr "条目" -#: module/blog/extensions/tags.py:12 -msgid "tags" -msgstr "标签" - -#: module/blog/extensions/translations.py:20 -#: module/extensions/translations.py:126 -msgid "translation of" -msgstr "翻译" - -#: module/blog/extensions/translations.py:23 -#: module/extensions/translations.py:129 -msgid "Leave this empty for entries in the primary language." -msgstr "在主要语言项中对这些条目留空。" - -#: module/blog/extensions/translations.py:44 -#: templates/admin/feincms/item_editor.html:43 -msgid "available translations" -msgstr "已有翻译" - -#: module/extensions/changedate.py:32 +#: module/extensions/changedate.py:41 msgid "creation date" msgstr "创建日期" -#: module/extensions/changedate.py:33 +#: module/extensions/changedate.py:43 msgid "modification date" msgstr "修改日期" -#: module/extensions/ct_tracker.py:140 +#: module/extensions/ct_tracker.py:156 msgid "content types" msgstr "内容类型" -#: module/extensions/datepublisher.py:67 +#: module/extensions/datepublisher.py:86 msgid "publication date" msgstr "发布起始日期" -#: module/extensions/datepublisher.py:70 +#: module/extensions/datepublisher.py:90 msgid "publication end date" msgstr "发布结束日期" -#: module/extensions/datepublisher.py:72 +#: module/extensions/datepublisher.py:93 msgid "Leave empty if the entry should stay active forever." msgstr "如果条目要永久显示,请留空" -#: module/extensions/datepublisher.py:103 +#: module/extensions/datepublisher.py:128 msgid "visible from - to" msgstr "可查看日期" -#: module/extensions/datepublisher.py:113 +#: module/extensions/datepublisher.py:139 msgid "Date-based publishing" msgstr "发布基于日期控制" -#: module/extensions/featured.py:9 +#: module/extensions/featured.py:15 msgid "featured" msgstr "特性" -#: module/extensions/featured.py:14 +#: module/extensions/featured.py:21 msgid "Featured" msgstr "特性" -#: module/extensions/seo.py:9 +#: module/extensions/seo.py:16 msgid "meta keywords" msgstr "meta 关键词" -#: module/extensions/seo.py:10 -msgid "This will be prepended to the default keyword list." -msgstr "这将作为默认的关键字列表被添加。" +#: module/extensions/seo.py:18 +msgid "Keywords are ignored by most search engines." +msgstr "" -#: module/extensions/seo.py:11 +#: module/extensions/seo.py:20 msgid "meta description" msgstr "meta 描述" -#: module/extensions/seo.py:12 -msgid "This will be prepended to the default description." -msgstr "这将作为默认的描述被添加" +#: module/extensions/seo.py:22 +msgid "" +"This text is displayed on the search results page. It is however not used " +"for the SEO ranking. Text longer than 140 characters is truncated." +msgstr "" -#: module/extensions/seo.py:17 +#: module/extensions/seo.py:32 msgid "Search engine optimization" msgstr "搜索引擎优化" -#: module/extensions/translations.py:207 +#: module/extensions/translations.py:249 msgid "Edit translation" msgstr "编辑翻译" -#: module/extensions/translations.py:210 +#: module/extensions/translations.py:256 msgid "Create translation" msgstr "创建翻译" -#: module/extensions/translations.py:215 +#: module/extensions/translations.py:264 msgid "translations" msgstr "翻译" -#: module/medialibrary/forms.py:26 +#: module/medialibrary/forms.py:31 msgid "This would create a loop in the hierarchy" msgstr "" -#: module/medialibrary/forms.py:64 +#: module/medialibrary/forms.py:77 #, python-format msgid "" "Cannot overwrite with different file type (attempt to overwrite a " "%(old_ext)s with a %(new_ext)s)" msgstr "" -#: module/medialibrary/modeladmins.py:58 +#: module/medialibrary/modeladmins.py:63 #, python-format msgid "Successfully added %(count)d media file to %(category)s." msgid_plural "Successfully added %(count)d media files to %(category)s." msgstr[0] "Successfully added %(count)d media files to %(category)s." -#: module/medialibrary/modeladmins.py:77 +#: module/medialibrary/modeladmins.py:84 msgid "Add selected media files to category" msgstr "所选的媒体文件添加到类" -#: module/medialibrary/modeladmins.py:86 +#: module/medialibrary/modeladmins.py:94 #, python-format msgid "ZIP file exported as %s" msgstr "" -#: module/medialibrary/modeladmins.py:88 +#: module/medialibrary/modeladmins.py:96 #, python-format msgid "ZIP file export failed: %s" msgstr "" -#: module/medialibrary/modeladmins.py:93 +#: module/medialibrary/modeladmins.py:104 msgid "Export selected media files as zip file" msgstr "" -#: module/medialibrary/modeladmins.py:136 +#: module/medialibrary/modeladmins.py:157 #: templates/admin/feincms/page/page/item_editor.html:15 msgid "Preview" msgstr "预览" -#: module/medialibrary/modeladmins.py:141 module/medialibrary/models.py:88 +#: module/medialibrary/modeladmins.py:162 module/medialibrary/models.py:103 msgid "file size" msgstr "文件大小" -#: module/medialibrary/modeladmins.py:146 module/medialibrary/models.py:86 +#: module/medialibrary/modeladmins.py:167 module/medialibrary/models.py:100 msgid "created" msgstr "创建" -#: module/medialibrary/modeladmins.py:165 module/medialibrary/models.py:85 +#: module/medialibrary/modeladmins.py:187 module/medialibrary/models.py:97 msgid "file type" msgstr "文件类型" -#: module/medialibrary/modeladmins.py:186 +#: module/medialibrary/modeladmins.py:211 msgid "file info" msgstr "文件信息" -#: module/medialibrary/modeladmins.py:198 +#: module/medialibrary/modeladmins.py:224 #, python-format msgid "%d files imported" msgstr "导入 %d 个文件" -#: module/medialibrary/modeladmins.py:200 +#: module/medialibrary/modeladmins.py:226 #, python-format msgid "ZIP import failed: %s" msgstr "" -#: module/medialibrary/modeladmins.py:202 +#: module/medialibrary/modeladmins.py:228 msgid "No input file given" msgstr "没有选择文件" -#: module/medialibrary/models.py:43 +#: module/medialibrary/models.py:49 msgid "parent" msgstr "父" -#: module/medialibrary/models.py:45 module/page/models.py:171 +#: module/medialibrary/models.py:51 module/page/models.py:175 msgid "slug" msgstr "slug" -#: module/medialibrary/models.py:49 +#: module/medialibrary/models.py:55 msgid "category" msgstr "类别" -#: module/medialibrary/models.py:50 module/medialibrary/models.py:90 +#: module/medialibrary/models.py:56 module/medialibrary/models.py:106 msgid "categories" msgstr "类别" -#: module/medialibrary/models.py:87 +#: module/medialibrary/models.py:101 msgid "copyright" msgstr "版权" -#: module/medialibrary/models.py:202 +#: module/medialibrary/models.py:219 msgid "Image" msgstr "图片" -#: module/medialibrary/models.py:203 +#: module/medialibrary/models.py:221 msgid "Video" msgstr "视频" -#: module/medialibrary/models.py:204 +#: module/medialibrary/models.py:224 msgid "Audio" msgstr "音频" -#: module/medialibrary/models.py:205 +#: module/medialibrary/models.py:226 msgid "PDF document" msgstr "PDF文档" -#: module/medialibrary/models.py:206 +#: module/medialibrary/models.py:227 msgid "Flash" msgstr "Flash" -#: module/medialibrary/models.py:207 +#: module/medialibrary/models.py:228 msgid "Text" msgstr "纯文本" -#: module/medialibrary/models.py:208 +#: module/medialibrary/models.py:229 msgid "Rich Text" msgstr "富文本" -#: module/medialibrary/models.py:209 +#: module/medialibrary/models.py:230 msgid "Zip archive" msgstr "zip压缩包" -#: module/medialibrary/models.py:210 +#: module/medialibrary/models.py:231 msgid "Microsoft Word" msgstr "Microsoft Word" -#: module/medialibrary/models.py:211 +#: module/medialibrary/models.py:233 msgid "Microsoft Excel" msgstr "Microsoft Excel" -#: module/medialibrary/models.py:212 +#: module/medialibrary/models.py:235 msgid "Microsoft PowerPoint" msgstr "Microsoft PowerPoint" -#: module/medialibrary/models.py:213 +#: module/medialibrary/models.py:237 msgid "Binary" msgstr "二进制文件" -#: module/medialibrary/models.py:232 +#: module/medialibrary/models.py:261 msgid "description" msgstr "描述" -#: module/medialibrary/models.py:235 +#: module/medialibrary/models.py:264 msgid "media file translation" msgstr "媒体文件翻译" -#: module/medialibrary/models.py:236 +#: module/medialibrary/models.py:265 msgid "media file translations" msgstr "媒体文件翻译" -#: module/page/forms.py:172 +#: module/page/extensions/excerpt.py:18 +msgid "excerpt" +msgstr "摘抄" + +#: module/page/extensions/excerpt.py:21 +msgid "Add a brief excerpt summarizing the content of this page." +msgstr "添加一条简要形容页面内容" + +#: module/page/extensions/excerpt.py:25 +msgid "Excerpt" +msgstr "摘抄" + +#: module/page/extensions/navigation.py:86 +#: module/page/extensions/navigation.py:115 +msgid "navigation extension" +msgstr "导航扩展" + +#: module/page/extensions/navigation.py:119 +msgid "" +"Select the module providing subpages for this page if you need to customize " +"the navigation." +msgstr "如果您需要定制的导航,选择提供此页面的子页面模块。" + +#: module/page/extensions/navigation.py:134 +msgid "Navigation extension" +msgstr "导航扩展" + +#: module/page/extensions/navigationgroups.py:20 +msgid "Default" +msgstr "" + +#: module/page/extensions/navigationgroups.py:21 +msgid "Footer" +msgstr "" + +#: module/page/extensions/navigationgroups.py:28 +#, fuzzy +#| msgid "in navigation" +msgid "navigation group" +msgstr "导航中" + +#: module/page/extensions/relatedpages.py:21 +msgid "Select pages that should be listed as related content." +msgstr "选择页面作为相关页面在列表中显示" + +#: module/page/extensions/relatedpages.py:26 +msgid "Related pages" +msgstr "相关页面" + +#: module/page/extensions/sites.py:21 +msgid "Site" +msgstr "网站" + +#: module/page/extensions/symlinks.py:22 +msgid "symlinked page" +msgstr "链接页面" + +#: module/page/extensions/symlinks.py:23 +msgid "All content is inherited from this page if given." +msgstr "所有内容继承于给出的页面" + +#: module/page/extensions/titles.py:19 +msgid "content title" +msgstr "内容标题" + +#: module/page/extensions/titles.py:22 +msgid "The first line is the main title, the following lines are subtitles." +msgstr "正文第一行作为主标题,第二行作为副标题" + +#: module/page/extensions/titles.py:26 +msgid "page title" +msgstr "页面标题" + +#: module/page/extensions/titles.py:30 +#, fuzzy +#| msgid "Page title for browser window. Same as title by default." +msgid "" +"Page title for browser window. Same as title bydefault. Must not be longer " +"than 70 characters." +msgstr "浏览器窗口显示标题默认为页面标题" + +#: module/page/extensions/titles.py:60 +msgid "Titles" +msgstr "标题" + +#: module/page/forms.py:187 msgid "This URL is already taken by an active page." msgstr "此URL已被其他页面使用" -#: module/page/forms.py:190 +#: module/page/forms.py:206 msgid "This URL is already taken by another active page." msgstr "此URL已被另一个页面使用" -#: module/page/modeladmins.py:41 +#: module/page/forms.py:211 +msgid "This page does not allow attachment of child pages" +msgstr "" + +#: module/page/modeladmins.py:42 msgid "Other options" msgstr "其他选项" -#: module/page/modeladmins.py:90 module/page/models.py:174 +#: module/page/modeladmins.py:80 module/page/models.py:182 msgid "in navigation" msgstr "导航中" -#: module/page/modeladmins.py:105 +#: module/page/modeladmins.py:109 module/page/modeladmins.py:111 msgid "Add child page" msgstr "增加子页面" -#: module/page/modeladmins.py:107 +#: module/page/modeladmins.py:120 module/page/modeladmins.py:122 #: templates/admin/feincms/content_inline.html:9 msgid "View on site" msgstr "站内查看" -#: module/page/modeladmins.py:126 +#: module/page/modeladmins.py:142 #, python-format -msgid "Add %(language)s Translation of \"%(page)s\"" +msgid "Add %(language)s translation of \"%(page)s\"" msgstr "" -#: module/page/modeladmins.py:156 +#: module/page/modeladmins.py:177 msgid "" "The content from the original translation has been copied to the newly " "created page." msgstr "" -#: module/page/modeladmins.py:170 +#: module/page/modeladmins.py:197 msgid "You don't have the necessary permissions to edit this object" msgstr "你无权编辑该对象" -#: module/page/modeladmins.py:185 +#: module/page/modeladmins.py:223 msgid "inherited" msgstr "继承" -#: module/page/modeladmins.py:189 +#: module/page/modeladmins.py:229 msgid "extensions" msgstr "扩展" -#: module/page/modeladmins.py:193 module/page/models.py:206 +#: module/page/modeladmins.py:233 module/page/models.py:221 msgid "is active" msgstr "激活" -#: module/page/models.py:167 +#: module/page/models.py:169 msgid "active" msgstr "激活" -#: module/page/models.py:175 +#: module/page/models.py:173 +#, fuzzy +#| msgid "This is used for the generated navigation too." +msgid "This title is also used for navigation menu items." +msgstr "也被用于产生导航条" + +#: module/page/models.py:176 +msgid "This is used to build the URL for this page" +msgstr "" + +#: module/page/models.py:184 msgid "override URL" msgstr "URL覆盖" -#: module/page/models.py:176 +#: module/page/models.py:186 msgid "" "Override the target URL. Be sure to include slashes at the beginning and at " -"the end if it is a local URL. This affects both the navigation and subpages'" -" URLs." -msgstr "覆盖目标URL,如果它是一个本地的URL,一定要包括在开始和结束时的斜线。这会影响导航和子页面的URL" +"the end if it is a local URL. This affects both the navigation and subpages' " +"URLs." +msgstr "" +"覆盖目标URL,如果它是一个本地的URL,一定要包括在开始和结束时的斜线。这会影响" +"导航和子页面的URL" -#: module/page/models.py:177 +#: module/page/models.py:190 msgid "redirect to" msgstr "转向" -#: module/page/models.py:178 +#: module/page/models.py:193 msgid "Target URL for automatic redirects or the primary key of a page." msgstr "" -#: module/page/models.py:180 +#: module/page/models.py:196 msgid "Cached URL" msgstr "URL缓存" -#: module/page/models.py:395 +#: module/page/models.py:298 +#, python-format +msgid "" +"This %(page_class)s uses a singleton template, and " +"FEINCMS_SINGLETON_TEMPLATE_DELETION_ALLOWED=False" +msgstr "" + +#: module/page/models.py:426 msgid "page" msgstr "页面" -#: module/page/models.py:396 +#: module/page/models.py:427 msgid "pages" msgstr "页面" -#: module/page/extensions/excerpt.py:9 -msgid "excerpt" -msgstr "摘抄" - -#: module/page/extensions/excerpt.py:10 -msgid "Add a brief excerpt summarizing the content of this page." -msgstr "添加一条简要形容页面内容" - -#: module/page/extensions/excerpt.py:12 -msgid "Excerpt" -msgstr "摘抄" - -#: module/page/extensions/navigation.py:82 -#: module/page/extensions/navigation.py:107 -msgid "navigation extension" -msgstr "导航扩展" - -#: module/page/extensions/navigation.py:110 -msgid "" -"Select the module providing subpages for this page if you need to customize " -"the navigation." -msgstr "如果您需要定制的导航,选择提供此页面的子页面模块。" - -#: module/page/extensions/navigation.py:125 -msgid "Navigation extension" -msgstr "导航扩展" - -#: module/page/extensions/relatedpages.py:14 -msgid "Select pages that should be listed as related content." -msgstr "选择页面作为相关页面在列表中显示" - -#: module/page/extensions/relatedpages.py:19 -msgid "Related pages" -msgstr "相关页面" - -#: module/page/extensions/sites.py:16 -msgid "Site" -msgstr "网站" - -#: module/page/extensions/symlinks.py:15 -msgid "symlinked page" -msgstr "链接页面" - -#: module/page/extensions/symlinks.py:16 -msgid "All content is inherited from this page if given." -msgstr "所有内容继承于给出的页面" - -#: module/page/extensions/titles.py:13 -msgid "content title" -msgstr "内容标题" - -#: module/page/extensions/titles.py:14 -msgid "The first line is the main title, the following lines are subtitles." -msgstr "正文第一行作为主标题,第二行作为副标题" - -#: module/page/extensions/titles.py:15 -msgid "page title" -msgstr "页面标题" - -#: module/page/extensions/titles.py:16 -msgid "Page title for browser window. Same as title by default." -msgstr "浏览器窗口显示标题默认为页面标题" - -#: module/page/extensions/titles.py:43 -msgid "Titles" -msgstr "标题" - -#: templates/admin/filter.html:3 -#, python-format -msgid " By %(filter_title)s " -msgstr "由 %(filter_title)s" - #: templates/admin/feincms/_messages_js.html:4 msgid "Really delete item?" msgstr "真的要删除?" @@ -770,9 +798,8 @@ msgstr "真的要修改模板么?
    全部修改已被保存。" #: templates/admin/feincms/_messages_js.html:9 #, python-format msgid "" -"Really change template?
    All changes are saved and content from " -"%%(source_regions)s is moved to " -"%%(target_region)s." +"Really change template?
    All changes are saved and content from " +"%%(source_regions)s is moved to %%(target_region)s." msgstr "" #: templates/admin/feincms/_messages_js.html:12 @@ -813,16 +840,16 @@ msgstr "父站的内容已自动继承。添加新内容会覆盖。" msgid "Add new item" msgstr "新建" -#: templates/admin/feincms/content_inline.html:91 +#: templates/admin/feincms/content_inline.html:93 #, python-format msgid "Add another %(verbose_name)s" msgstr "添加另一个 %(verbose_name)s" -#: templates/admin/feincms/content_inline.html:94 +#: templates/admin/feincms/content_inline.html:96 msgid "Remove" msgstr "移除" -#: templates/admin/feincms/fe_editor.html:40 +#: templates/admin/feincms/fe_editor.html:30 msgid "Save" msgstr "保存" @@ -850,13 +877,21 @@ msgstr "下" msgid "remove" msgstr "移除" -#: templates/admin/feincms/recover_form.html:8 -#: templates/admin/feincms/revision_form.html:8 +#: templates/admin/feincms/page/page/item_editor.html:10 +msgid "Edit on site" +msgstr "站内编辑" + #: templates/admin/feincms/page/page/item_editor.html:23 #: templates/admin/feincms/page/page/tree_editor.html:7 +#: templates/admin/feincms/recover_form.html:8 +#: templates/admin/feincms/revision_form.html:8 msgid "Home" msgstr "首页" +#: templates/admin/feincms/page/page/item_editor.html:27 +msgid "Add" +msgstr "添加" + #: templates/admin/feincms/recover_form.html:11 #, python-format msgid "Recover deleted %(verbose_name)s" @@ -879,29 +914,26 @@ msgstr "Revert %(verbose_name)s" msgid "Press the save button below to revert to this version of the object." msgstr "按下面的“保存”按钮,恢复到这个版本的对象。" -#: templates/admin/feincms/tree_editor.html:32 +#: templates/admin/feincms/tree_editor.html:22 msgid "Shortcuts" msgstr "快捷方式" -#: templates/admin/feincms/tree_editor.html:34 +#: templates/admin/feincms/tree_editor.html:24 msgid "Collapse tree" msgstr "折叠树型" -#: templates/admin/feincms/tree_editor.html:35 +#: templates/admin/feincms/tree_editor.html:25 msgid "Expand tree" msgstr "展开树型" -#: templates/admin/feincms/tree_editor.html:38 +#: templates/admin/feincms/tree_editor.html:29 msgid "Filter" msgstr "过滤器" -#: templates/admin/feincms/page/page/item_editor.html:10 -msgid "Edit on site" -msgstr "站内编辑" - -#: templates/admin/feincms/page/page/item_editor.html:27 -msgid "Add" -msgstr "添加" +#: templates/admin/filter.html:3 +#, python-format +msgid " By %(filter_title)s " +msgstr "由 %(filter_title)s" #: templates/admin/medialibrary/add_to_category.html:5 #: templates/admin/medialibrary/add_to_category.html:9 @@ -945,9 +977,14 @@ msgstr "%(comment_count)s 条评论" #, python-format msgid "" "\n" -" %(comment_username)s said on %(comment_submit_date)s
    \n" +" %(comment_username)s said on %(comment_submit_date)s
    \n" +" " +msgstr "" +"\n" +" %(comment_username)s 在 %(comment_submit_date)s 说
    \n" " " -msgstr "\n %(comment_username)s 在 %(comment_submit_date)s 说
    \n " #: templates/content/comments/default.html:28 msgid "No comments." @@ -964,3 +1001,27 @@ msgstr "提交" #: templates/content/contactform/thanks.html:3 msgid "Thanks!" msgstr "感谢" + +#~ msgid "plain" +#~ msgstr "无格式" + +#~ msgid "title row" +#~ msgstr "标题行" + +#~ msgid "title row and column" +#~ msgstr "标题行和列" + +#~ msgid "table" +#~ msgstr "表格" + +#~ msgid "tables" +#~ msgstr "表格" + +#~ msgid "data" +#~ msgstr "数据" + +#~ msgid "This will be prepended to the default keyword list." +#~ msgstr "这将作为默认的关键字列表被添加。" + +#~ msgid "This will be prepended to the default description." +#~ msgstr "这将作为默认的描述被添加" diff --git a/feincms/management/checker.py b/feincms/management/checker.py deleted file mode 100644 index 3336d4c25..000000000 --- a/feincms/management/checker.py +++ /dev/null @@ -1,66 +0,0 @@ -from __future__ import absolute_import, print_function, unicode_literals - -from django.core.management.color import color_style -from django.db import connection - - -def check_database_schema(cls, module_name): - """ - Returns a function which inspects the database table of the passed class. - It checks whether all fields in the model are available on the database - too. This is especially helpful for models with an extension mechanism, - where the extension might be activated after syncdb has been run for the - first time. - - Please note that you have to connect the return value using strong - references. Here's an example how to do this:: - - signals.post_syncdb.connect( - check_database_schema(Page, __name__), weak=False) - - (Yes, this is a weak attempt at a substitute for South until we find - a way to make South work with FeinCMS' dynamic model creation.) - """ - - def _fn(sender, **kwargs): - if sender.__name__ != module_name: - return - - cursor = connection.cursor() - - existing_columns = [ - row[0] - for row in connection.introspection.get_table_description( - cursor, cls._meta.db_table) - ] - - missing_columns = [] - - for field in cls._meta.fields: - if field.column not in existing_columns: - missing_columns.append(field) - - if not missing_columns: - return - - style = color_style() - - print(style.ERROR( - 'The following columns seem to be missing in the database table' - ' %s:' % cls._meta.db_table)) - - for field in missing_columns: - print('%s:%s%s' % ( - style.SQL_KEYWORD(field.column), - ' ' * (25 - len(field.column)), - '%s.%s' % ( - field.__class__.__module__, field.__class__.__name__), - )) - - print(style.NOTICE( - '\nPlease consult the output of `python manage.py sql %s` to' - ' find out what the correct column types are. (Or use south,' - ' which is what you should be doing anyway.)\n' % ( - cls._meta.app_label))) - - return _fn diff --git a/feincms/management/commands/feincms_validate.py b/feincms/management/commands/feincms_validate.py deleted file mode 100644 index dbf681b9b..000000000 --- a/feincms/management/commands/feincms_validate.py +++ /dev/null @@ -1,54 +0,0 @@ -# ------------------------------------------------------------------------ -# coding=utf-8 -# ------------------------------------------------------------------------ -""" -``feincms_validate`` --------------------- - -``feincms_validate`` checks your models for common pitfalls. -""" -from __future__ import absolute_import, print_function, unicode_literals - -from django.core.management.base import NoArgsCommand -from django.core.management.color import color_style -from django.db.models import loading - - -class Command(NoArgsCommand): - help = "Check models for common pitfalls." - - requires_model_validation = False - - def handle_noargs(self, **options): - self.style = color_style() - - print("Running Django's own validation:") - self.validate(display_num_errors=True) - - for model in loading.get_models(): - if hasattr(model, '_create_content_base'): - self.validate_base_model(model) - - if hasattr(model, '_feincms_content_models'): - self.validate_content_type(model) - - def validate_base_model(self, model): - """ - Validate a subclass of ``feincms.models.Base`` or anything else - created by ``feincms.models.create_base_model`` - """ - - if not hasattr(model, 'template'): - print(self.style.NOTICE( - '%s has no template attribute; did you forget' - ' register_templates or register_regions?' % model)) - - def validate_content_type(self, model): - """ - Validate a dynamically created concrete content type - """ - - for base in model.__bases__: - if not base._meta.abstract: - print(self.style.NOTICE( - 'One of %s bases, %s, is not abstract' % (model, base))) diff --git a/feincms/management/commands/medialibrary_orphans.py b/feincms/management/commands/medialibrary_orphans.py index 599c910e6..208d47213 100644 --- a/feincms/management/commands/medialibrary_orphans.py +++ b/feincms/management/commands/medialibrary_orphans.py @@ -1,22 +1,24 @@ -from __future__ import absolute_import, print_function, unicode_literals - import os -from django.core.management.base import NoArgsCommand -from django.utils.encoding import force_text +from django.conf import settings +from django.core.management.base import BaseCommand from feincms.module.medialibrary.models import MediaFile -class Command(NoArgsCommand): +class Command(BaseCommand): help = "Prints all orphaned files in the `media/medialibrary` folder" - def handle_noargs(self, **options): - mediafiles = list(MediaFile.objects.values_list('file', flat=True)) + def handle(self, **options): + mediafiles = list(MediaFile.objects.values_list("file", flat=True)) + + root_len = len(settings.MEDIA_ROOT) + medialib_path = os.path.join(settings.MEDIA_ROOT, "medialibrary") - # TODO make this smarter, and take MEDIA_ROOT into account - for base, dirs, files in os.walk('media/medialibrary'): + for base, dirs, files in os.walk(medialib_path): for f in files: - full = os.path.join(base[6:], f) - if force_text(full) not in mediafiles: - print(os.path.join(base, f)) + if base.startswith(settings.MEDIA_ROOT): + base = base[root_len:] + full = os.path.join(base, f) + if full not in mediafiles: + self.stdout.write(full) diff --git a/feincms/management/commands/medialibrary_to_filer.py b/feincms/management/commands/medialibrary_to_filer.py new file mode 100644 index 000000000..de0961e01 --- /dev/null +++ b/feincms/management/commands/medialibrary_to_filer.py @@ -0,0 +1,60 @@ +from django.contrib.auth.models import User +from django.core.files import File as DjangoFile +from django.core.management.base import BaseCommand +from filer.models import File, Image + +from feincms.contents import FilerFileContent, FilerImageContent +from feincms.module.medialibrary.contents import MediaFileContent +from feincms.module.medialibrary.models import MediaFile +from feincms.module.page.models import Page + + +PageMediaFileContent = Page.content_type_for(MediaFileContent) +PageFilerFileContent = Page.content_type_for(FilerFileContent) +PageFilerImageContent = Page.content_type_for(FilerImageContent) + + +assert all((PageMediaFileContent, PageFilerFileContent, PageFilerImageContent)), ( + "Not all required models available" +) + + +class Command(BaseCommand): + help = "Migrate the medialibrary and contents to django-filer" + + def handle(self, **options): + user = User.objects.order_by("pk")[0] + + count = MediaFile.objects.count() + + for i, mediafile in enumerate(MediaFile.objects.order_by("pk")): + model = Image if mediafile.type == "image" else File + content_model = ( + PageFilerImageContent + if mediafile.type == "image" + else PageFilerFileContent + ) # noqa + + filerfile = model.objects.create( + owner=user, + original_filename=mediafile.file.name, + file=DjangoFile(mediafile.file.file, name=mediafile.file.name), + ) + + contents = PageMediaFileContent.objects.filter(mediafile=mediafile) + + for content in contents: + content_model.objects.create( + parent=content.parent, + region=content.region, + ordering=content.ordering, + type=content.type, + mediafile=filerfile, + ) + + content.delete() + + if not i % 10: + self.stdout.write(f"{i} / {count} files\n") + + self.stdout.write(f"{count} / {count} files\n") diff --git a/feincms/management/commands/rebuild_mptt.py b/feincms/management/commands/rebuild_mptt.py index 7258bf9da..7c292758f 100644 --- a/feincms/management/commands/rebuild_mptt.py +++ b/feincms/management/commands/rebuild_mptt.py @@ -1,5 +1,4 @@ # ------------------------------------------------------------------------ -# coding=utf-8 # ------------------------------------------------------------------------ """ ``rebuild_mptt`` @@ -8,18 +7,17 @@ ``rebuild_mptt`` rebuilds your mptt pointers. Only use in emergencies. """ -from __future__ import absolute_import, print_function, unicode_literals - -from django.core.management.base import NoArgsCommand +from django.core.management.base import BaseCommand from feincms.module.page.models import Page -class Command(NoArgsCommand): - help = ( - "Run this manually to rebuild your mptt pointers. Only use in" - " emergencies.") +class Command(BaseCommand): + help = "Run this manually to rebuild your mptt pointers. Only use in emergencies." def handle_noargs(self, **options): - print("Rebuilding MPTT pointers for Page") + self.handle(**options) + + def handle(self, **options): + self.stdout.write("Rebuilding MPTT pointers for Page") Page._tree_manager.rebuild() diff --git a/feincms/management/commands/update_rsscontent.py b/feincms/management/commands/update_rsscontent.py deleted file mode 100644 index 24a37d12a..000000000 --- a/feincms/management/commands/update_rsscontent.py +++ /dev/null @@ -1,27 +0,0 @@ -""" -``update_rsscontent`` ---------------------- - -``update_rsscontent`` should be run as a cronjob when the ``RSSContent`` -content type is used -- the content type itself does not update the -feed by itself. -""" - -from __future__ import absolute_import, unicode_literals - -from django.core.management.base import BaseCommand - -from feincms.content.rss.models import RSSContent - - -class Command(BaseCommand): - help = "Run this as a cronjob." - - def handle(self, date_format='', *args, **options): - # find all concrete content types of RSSContent - for cls in RSSContent._feincms_content_models: - for content in cls.objects.all(): - if date_format: - content.cache_content(date_format=date_format) - else: - content.cache_content() diff --git a/feincms/models.py b/feincms/models.py index 7b3eb5779..5e65b2475 100644 --- a/feincms/models.py +++ b/feincms/models.py @@ -2,35 +2,28 @@ This is the core of FeinCMS All models defined here are abstract, which means no tables are created in -the feincms\_ namespace. +the feincms namespace. """ -from __future__ import absolute_import, unicode_literals - -from functools import reduce -import sys import operator +import sys import warnings +from collections import OrderedDict +from functools import reduce from django.contrib.contenttypes.models import ContentType from django.core.exceptions import ImproperlyConfigured from django.db import connections, models from django.db.models import Q -from django.db.models.fields import FieldDoesNotExist -from django.db.models.loading import get_model from django.forms.widgets import Media -from django.template.loader import render_to_string -from django.utils.datastructures import SortedDict -from django.utils.encoding import force_text, python_2_unicode_compatible -from django.utils.translation import ugettext_lazy as _ +from django.utils.encoding import force_str +from django.utils.translation import gettext_lazy as _ -from feincms import ensure_completely_loaded from feincms.extensions import ExtensionsMixin -from feincms.utils import copy_model_instance +from feincms.utils import ChoicesCharField, copy_model_instance -@python_2_unicode_compatible -class Region(object): +class Region: """ This class represents a region inside a template. Example regions might be 'main' and 'sidebar'. @@ -39,11 +32,11 @@ class Region(object): def __init__(self, key, title, *args): self.key = key self.title = title - self.inherited = args and args[0] == 'inherited' or False + self.inherited = args and args[0] == "inherited" or False self._content_types = [] def __str__(self): - return force_text(self.title) + return force_str(self.title) @property def content_types(self): @@ -53,20 +46,17 @@ def content_types(self): """ return [ - (ct.__name__.lower(), ct._meta.verbose_name) - for ct in self._content_types + (ct.__name__.lower(), ct._meta.verbose_name) for ct in self._content_types ] -@python_2_unicode_compatible -class Template(object): +class Template: """ A template is a standard Django template which is used to render a CMS object, most commonly a page. """ - def __init__(self, title, path, regions, key=None, preview_image=None, - **kwargs): + def __init__(self, title, path, regions, key=None, preview_image=None, **kwargs): # The key is what will be stored in the database. If key is undefined # use the template path as fallback. if not key: @@ -76,10 +66,10 @@ def __init__(self, title, path, regions, key=None, preview_image=None, self.title = title self.path = path self.preview_image = preview_image - self.singleton = kwargs.get('singleton', False) - self.child_template = kwargs.get('child_template', None) - self.enforce_leaf = kwargs.get('enforce_leaf', False) - self.urlconf = kwargs.get('urlconf', None) + self.singleton = kwargs.get("singleton", False) + self.child_template = kwargs.get("child_template", None) + self.enforce_leaf = kwargs.get("enforce_leaf", False) + self.urlconf = kwargs.get("urlconf", None) def _make_region(data): if isinstance(data, Region): @@ -87,13 +77,13 @@ def _make_region(data): return Region(*data) self.regions = [_make_region(row) for row in regions] - self.regions_dict = dict((r.key, r) for r in self.regions) + self.regions_dict = {r.key: r for r in self.regions} def __str__(self): - return force_text(self.title) + return force_str(self.title) -class ContentProxy(object): +class ContentProxy: """ The ``ContentProxy`` is responsible for loading the content blocks for all regions (including content blocks in inherited regions) and assembling @@ -108,9 +98,7 @@ def __init__(self, item): item._needs_content_types() self.item = item self.db = item._state.db - self._cache = { - 'cts': {}, - } + self._cache = {"cts": {}} def _inherit_from(self): """ @@ -121,8 +109,7 @@ def _inherit_from(self): is good enough (tm) for pages. """ - return self.item.get_ancestors(ascending=True).values_list( - 'pk', flat=True) + return self.item.get_ancestors(ascending=True).values_list("pk", flat=True) def _fetch_content_type_counts(self): """ @@ -141,7 +128,7 @@ def _fetch_content_type_counts(self): } """ - if 'counts' not in self._cache: + if "counts" not in self._cache: counts = self._fetch_content_type_count_helper(self.item.pk) empty_inherited_regions = set() @@ -152,7 +139,8 @@ def _fetch_content_type_counts(self): if empty_inherited_regions: for parent in self._inherit_from(): parent_counts = self._fetch_content_type_count_helper( - parent, regions=tuple(empty_inherited_regions)) + parent, regions=tuple(empty_inherited_regions) + ) counts.update(parent_counts) for key in parent_counts.keys(): @@ -161,40 +149,38 @@ def _fetch_content_type_counts(self): if not empty_inherited_regions: break - self._cache['counts'] = counts - return self._cache['counts'] + self._cache["counts"] = counts + return self._cache["counts"] def _fetch_content_type_count_helper(self, pk, regions=None): - tmpl = [ - 'SELECT %d AS ct_idx, region, COUNT(id) FROM %s WHERE parent_id=%s' - ] + tmpl = ["SELECT %d AS ct_idx, region, COUNT(id) FROM %s WHERE parent_id=%s"] args = [] if regions: - tmpl.append( - 'AND region IN (' + ','.join(['%%s'] * len(regions)) + ')') + tmpl.append("AND region IN (" + ",".join(["%%s"] * len(regions)) + ")") args.extend(regions * len(self.item._feincms_content_types)) - tmpl.append('GROUP BY region') - tmpl = ' '.join(tmpl) + tmpl.append("GROUP BY region") + tmpl = " ".join(tmpl) - sql = ' UNION '.join([ - tmpl % (idx, cls._meta.db_table, pk) - for idx, cls in enumerate(self.item._feincms_content_types) - ]) - sql = 'SELECT * FROM ( ' + sql + ' ) AS ct ORDER BY ct_idx' - - cursor = connections[self.db].cursor() - cursor.execute(sql, args) + sql = " UNION ".join( + [ + tmpl % (idx, cls._meta.db_table, pk) + for idx, cls in enumerate(self.item._feincms_content_types) + ] + ) + sql = "SELECT * FROM ( " + sql + " ) AS ct ORDER BY ct_idx" _c = {} - for ct_idx, region, count in cursor.fetchall(): - if count: - _c.setdefault(region, []).append((pk, ct_idx)) + with connections[self.db].cursor() as cursor: + cursor.execute(sql, args) + for ct_idx, region, count in cursor.fetchall(): + if count: + _c.setdefault(region, []).append((pk, ct_idx)) return _c - def _popuplate_content_type_caches(self, types): + def _populate_content_type_caches(self, types): """ Populate internal caches for all content types passed """ @@ -203,48 +189,61 @@ def _popuplate_content_type_caches(self, types): for region, counts in self._fetch_content_type_counts().items(): for pk, ct_idx in counts: counts_by_type.setdefault( - self.item._feincms_content_types[ct_idx], - [], + self.item._feincms_content_types[ct_idx], [] ).append((region, pk)) # Resolve abstract to concrete content types - content_types = ( - cls for cls in self.item._feincms_content_types - if issubclass(cls, tuple(types)) - ) + if types is self.item._feincms_content_types: + # If we come from _fetch_regions, we don't need to do + # any type resolving + content_types = self.item._feincms_content_types + else: + content_types = ( + cls + for cls in self.item._feincms_content_types + if issubclass(cls, tuple(types)) + ) for cls in content_types: counts = counts_by_type.get(cls) - if cls not in self._cache['cts']: + if cls not in self._cache["cts"]: if counts: - self._cache['cts'][cls] = list(cls.get_queryset(reduce( - operator.or_, - (Q(region=r[0], parent=r[1]) for r in counts) - ))) + self._cache["cts"][cls] = list( + cls.get_queryset().filter( + reduce( + operator.or_, + (Q(region=r[0], parent=r[1]) for r in counts), + ) + ) + ) else: - self._cache['cts'][cls] = [] + self._cache["cts"][cls] = [] + + # share this content proxy object between all content items + # so that each can use obj.parent.content to determine its + # relationship to its siblings, etc. + for objects in self._cache["cts"].values(): + for obj in objects: + setattr(obj.parent, "_content_proxy", self) def _fetch_regions(self): """ Fetches all content types and group content types into regions """ - if 'regions' not in self._cache: - self._popuplate_content_type_caches( - self.item._feincms_content_types) + if "regions" not in self._cache: + self._populate_content_type_caches(self.item._feincms_content_types) contents = {} - for cls, content_list in self._cache['cts'].items(): + for content_list in self._cache["cts"].values(): for instance in content_list: contents.setdefault(instance.region, []).append(instance) - self._cache['regions'] = dict( - ( - region, - sorted(instances, key=lambda c: c.ordering), - ) for region, instances in contents.items() - ) + self._cache["regions"] = { + region: sorted(instances, key=lambda c: c.ordering) + for region, instances in contents.items() + } - return self._cache['regions'] + return self._cache["regions"] def all_of_type(self, type_or_tuple): """ @@ -258,11 +257,11 @@ def all_of_type(self, type_or_tuple): """ content_list = [] - if not hasattr(type_or_tuple, '__iter__'): + if not hasattr(type_or_tuple, "__iter__"): type_or_tuple = (type_or_tuple,) - self._popuplate_content_type_caches(type_or_tuple) + self._populate_content_type_caches(type_or_tuple) - for type, contents in self._cache['cts'].items(): + for type, contents in self._cache["cts"].items(): if any(issubclass(type, t) for t in type_or_tuple): content_list.extend(contents) @@ -273,16 +272,17 @@ def _get_media(self): Collect the media files of all content types of the current object """ - if 'media' not in self._cache: + if "media" not in self._cache: media = Media() for contents in self._fetch_regions().values(): for content in contents: - if hasattr(content, 'media'): + if hasattr(content, "media"): media = media + content.media - self._cache['media'] = media - return self._cache['media'] + self._cache["media"] = media + return self._cache["media"] + media = property(_get_media) def __getattr__(self, attr): @@ -293,7 +293,7 @@ def __getattr__(self, attr): has the inherited flag set, this method will go up the ancestor chain until either some item contents have found or no ancestors are left. """ - if (attr.startswith('__')): + if attr.startswith("__"): raise AttributeError # Do not trigger loading of real content type models if not necessary @@ -333,15 +333,15 @@ def register_regions(cls, *regions): ) """ - if hasattr(cls, 'template'): + if hasattr(cls, "template"): warnings.warn( - 'Ignoring second call to register_regions.', - RuntimeWarning) + "Ignoring second call to register_regions.", RuntimeWarning + ) return # implicitly creates a dummy template object -- the item editor # depends on the presence of a template. - cls.template = Template('', '', regions) + cls.template = Template("", "", regions) cls._feincms_all_regions = cls.template.regions @classmethod @@ -370,8 +370,8 @@ def register_templates(cls, *templates): }) """ - if not hasattr(cls, '_feincms_templates'): - cls._feincms_templates = SortedDict() + if not hasattr(cls, "_feincms_templates"): + cls._feincms_templates = OrderedDict() cls.TEMPLATES_CHOICES = [] instances = cls._feincms_templates @@ -383,32 +383,47 @@ def register_templates(cls, *templates): instances[template.key] = template try: - field = cls._meta.get_field_by_name('template_key')[0] - except (FieldDoesNotExist, IndexError): + field = next( + iter( + field + for field in cls._meta.local_fields + if field.name == "template_key" + ) + ) + except (StopIteration,): cls.add_to_class( - 'template_key', - models.CharField(_('template'), max_length=255, choices=()) + "template_key", + ChoicesCharField( + _("template"), + max_length=255, + ), + ) + field = next( + iter( + field + for field in cls._meta.local_fields + if field.name == "template_key" + ) ) - field = cls._meta.get_field_by_name('template_key')[0] def _template(self): - ensure_completely_loaded() - try: return self._feincms_templates[self.template_key] except KeyError: # return first template as a fallback if the template # has changed in-between return self._feincms_templates[ - list(self._feincms_templates.keys())[0]] + list(self._feincms_templates.keys())[0] + ] cls.template = property(_template) - cls.TEMPLATE_CHOICES = field._choices = [ - (template_.key, template_.title,) + cls.TEMPLATE_CHOICES = [ + (template_.key, template_.title) for template_ in cls._feincms_templates.values() ] - field.default = field.choices[0][0] + field.choices = cls.TEMPLATE_CHOICES + field.default = cls.TEMPLATE_CHOICES[0][0] # Build a set of all regions used anywhere cls._feincms_all_regions = set() @@ -426,7 +441,7 @@ def content(self): ``content_proxy_class`` member variable. """ - if not hasattr(self, '_content_proxy'): + if not hasattr(self, "_content_proxy"): self._content_proxy = self.content_proxy_class(self) return self._content_proxy @@ -453,19 +468,18 @@ def _create_content_base(cls): class Meta: abstract = True app_label = cls._meta.app_label - ordering = ['ordering'] + ordering = ["ordering"] def __str__(self): - return ( - '%s, region=%s,' - ' ordering=%d>') % ( + return ("%s, region=%s, ordering=%d>") % ( self.__class__.__name__, self.pk, self.parent.__class__.__name__, self.parent.pk, self.parent, self.region, - self.ordering) + self.ordering, + ) def render(self, **kwargs): """ @@ -476,48 +490,18 @@ def render(self, **kwargs): time instead of adding region-specific render methods. """ - render_fn = getattr(self, 'render_%s' % self.region, None) + render_fn = getattr(self, "render_%s" % self.region, None) if render_fn: return render_fn(**kwargs) raise NotImplementedError - def fe_render(self, **kwargs): - """ - Frontend Editing enabled renderer - """ - - if 'request' in kwargs: - request = kwargs['request'] - - if (hasattr(request, 'COOKIES') - and request.COOKIES.get('frontend_editing')): - return render_to_string('admin/feincms/fe_box.html', { - 'content': self.render(**kwargs), - 'identifier': self.fe_identifier(), - }) - - return self.render(**kwargs) - - def fe_identifier(self): - """ - Returns an identifier which is understood by the frontend - editing javascript code. (It is used to find the URL which - should be used to load the form for every given block of - content.) - """ - - return '%s-%s-%s-%s-%s' % ( - cls._meta.app_label, - cls._meta.module_name, - self.__class__.__name__.lower(), - self.parent_id, - self.id, - ) - - def get_queryset(cls, filter_args): - return cls.objects.select_related().filter(filter_args) + def get_queryset(cls, filter_args=None): + qs = cls.objects.select_related() + if filter_args is not None: + return qs.filter(filter_args) + return qs attrs = { # The basic content type is put into @@ -526,31 +510,31 @@ def get_queryset(cls, filter_args): # needs to know where a model comes # from, therefore we ensure that the # module is always known. - '__module__': cls.__module__, - '__str__': __str__, - 'render': render, - 'fe_render': fe_render, - 'fe_identifier': fe_identifier, - 'get_queryset': classmethod(get_queryset), - 'Meta': Meta, - 'parent': models.ForeignKey(cls, related_name='%(class)s_set'), - 'region': models.CharField(max_length=255), - 'ordering': models.IntegerField(_('ordering'), default=0), + "__module__": cls.__module__, + "__str__": __str__, + "render": render, + "get_queryset": classmethod(get_queryset), + "Meta": Meta, + "parent": models.ForeignKey( + cls, related_name="%(class)s_set", on_delete=models.CASCADE + ), + "region": models.CharField(max_length=255), + "ordering": models.IntegerField(_("ordering"), default=0), } # create content base type and save reference on CMS class - name = '_Internal%sContentTypeBase' % cls.__name__ + name = "_Internal%sContentTypeBase" % cls.__name__ if hasattr(sys.modules[cls.__module__], name): warnings.warn( - 'The class %s.%s has the same name as the class that ' - 'FeinCMS auto-generates based on %s.%s. To avoid database' - 'errors and import clashes, rename one of these classes.' + "The class %s.%s has the same name as the class that " + "FeinCMS auto-generates based on %s.%s. To avoid database" + "errors and import clashes, rename one of these classes." % (cls.__module__, name, cls.__module__, cls.__name__), - RuntimeWarning) + RuntimeWarning, + ) - cls._feincms_content_model = python_2_unicode_compatible( - type(str(name), (models.Model,), attrs)) + cls._feincms_content_model = type(str(name), (models.Model,), attrs) # list of concrete content types cls._feincms_content_types = [] @@ -571,23 +555,24 @@ def get_queryset(cls, filter_args): # list of item editor context processors, will be extended by # content types - if hasattr(cls, 'feincms_item_editor_context_processors'): + if hasattr(cls, "feincms_item_editor_context_processors"): cls.feincms_item_editor_context_processors = list( - cls.feincms_item_editor_context_processors) + cls.feincms_item_editor_context_processors + ) else: cls.feincms_item_editor_context_processors = [] # list of templates which should be included in the item editor, # will be extended by content types - if hasattr(cls, 'feincms_item_editor_includes'): + if hasattr(cls, "feincms_item_editor_includes"): cls.feincms_item_editor_includes = dict( - cls.feincms_item_editor_includes) + cls.feincms_item_editor_includes + ) else: cls.feincms_item_editor_includes = {} @classmethod - def create_content_type(cls, model, regions=None, class_name=None, - **kwargs): + def create_content_type(cls, model, regions=None, class_name=None, **kwargs): """ This is the method you'll use to create concrete content types. @@ -636,50 +621,36 @@ def create_content_type(cls, model, regions=None, class_name=None, # content types with the same class name because of related_name # clashes try: - getattr(cls, '%s_set' % class_name.lower()) + getattr(cls, "%s_set" % class_name.lower()) warnings.warn( - 'Cannot create content type using %s.%s for %s.%s,' - ' because %s_set is already taken.' % ( - model.__module__, class_name, - cls.__module__, cls.__name__, - class_name.lower()), - RuntimeWarning) + "Cannot create content type using %s.%s for %s.%s," + " because %s_set is already taken." + % ( + model.__module__, + class_name, + cls.__module__, + cls.__name__, + class_name.lower(), + ), + RuntimeWarning, + ) return except AttributeError: # everything ok pass - # Next name clash test. Happens when the same content type is - # created for two Base subclasses living in the same Django - # application (github issues #73 and #150) - try: - other_model = get_model(cls._meta.app_label, class_name) - if other_model is None: - # Django 1.6 and earlier - raise LookupError - except LookupError: - pass - else: - warnings.warn( - 'It seems that the content type %s exists twice in %s.' - ' Use the class_name argument to create_content_type to' - ' avoid this error.' % ( - model.__name__, - cls._meta.app_label), - RuntimeWarning) - if not model._meta.abstract: raise ImproperlyConfigured( - 'Cannot create content type from' - ' non-abstract model (yet).') + "Cannot create content type from non-abstract model (yet)." + ) - if not hasattr(cls, '_feincms_content_model'): + if not hasattr(cls, "_feincms_content_model"): cls._create_content_base() feincms_content_base = cls._feincms_content_model class Meta(feincms_content_base.Meta): - db_table = '%s_%s' % (cls._meta.db_table, class_name.lower()) + db_table = f"{cls._meta.db_table}_{class_name.lower()}" verbose_name = model._meta.verbose_name verbose_name_plural = model._meta.verbose_name_plural permissions = model._meta.permissions @@ -692,28 +663,21 @@ class Meta(feincms_content_base.Meta): # content type may be used by several CMS # base models at the same time (f.e. in # the blog and the page module). - '__module__': cls.__module__, - 'Meta': Meta, + "__module__": cls.__module__, + "Meta": Meta, } - new_type = type( - str(class_name), - (model, feincms_content_base,), - attrs, - ) + new_type = type(str(class_name), (model, feincms_content_base), attrs) cls._feincms_content_types.append(new_type) - # For consistency's sake, also install the new type in the module - setattr(sys.modules[cls.__module__], class_name, new_type) - if hasattr(getattr(new_type, 'process', None), '__call__'): + if hasattr(getattr(new_type, "process", None), "__call__"): cls._feincms_content_types_with_process.append(new_type) - if hasattr(getattr(new_type, 'finalize', None), '__call__'): + if hasattr(getattr(new_type, "finalize", None), "__call__"): cls._feincms_content_types_with_finalize.append(new_type) # content types can be limited to a subset of regions if not regions: - regions = set([ - region.key for region in cls._feincms_all_regions]) + regions = {region.key for region in cls._feincms_all_regions} for region in cls._feincms_all_regions: if region.key in regions: @@ -724,7 +688,7 @@ class Meta(feincms_content_base.Meta): # f.e. for the update_rsscontent management command, which needs to # find all concrete RSSContent types, so that the RSS feeds can be # fetched - if not hasattr(model, '_feincms_content_models'): + if not hasattr(model, "_feincms_content_models"): model._feincms_content_models = [] model._feincms_content_models.append(new_type) @@ -734,36 +698,37 @@ class Meta(feincms_content_base.Meta): # Handle optgroup argument for grouping content types in the item # editor - optgroup = kwargs.pop('optgroup', None) + optgroup = kwargs.pop("optgroup", None) if optgroup: new_type.optgroup = optgroup # customization hook. - if hasattr(new_type, 'initialize_type'): + if hasattr(new_type, "initialize_type"): new_type.initialize_type(**kwargs) else: for k, v in kwargs.items(): setattr(new_type, k, v) # collect item editor context processors from the content type - if hasattr(model, 'feincms_item_editor_context_processors'): + if hasattr(model, "feincms_item_editor_context_processors"): cls.feincms_item_editor_context_processors.extend( - model.feincms_item_editor_context_processors) + model.feincms_item_editor_context_processors + ) # collect item editor includes from the content type - if hasattr(model, 'feincms_item_editor_includes'): + if hasattr(model, "feincms_item_editor_includes"): for key, incls in model.feincms_item_editor_includes.items(): - cls.feincms_item_editor_includes.setdefault( - key, set()).update(incls) + cls.feincms_item_editor_includes.setdefault(key, set()).update( + incls + ) - ensure_completely_loaded(force=True) return new_type @property def _django_content_type(self): - if not getattr(self, '_cached_django_content_type', None): - self.__class__._cached_django_content_type = ( - ContentType.objects.get_for_model(self)) + if not getattr(self, "_cached_django_content_type", None): + ct = ContentType.objects.get_for_model(self) + self.__class__._cached_django_content_type = ct return self.__class__._cached_django_content_type @classmethod @@ -775,8 +740,10 @@ def content_type_for(cls, model): concrete_type = Page.content_type_for(VideoContent) """ - if (not hasattr(cls, '_feincms_content_types') - or not cls._feincms_content_types): + if ( + not hasattr(cls, "_feincms_content_types") + or not cls._feincms_content_types + ): return None for type in cls._feincms_content_types: @@ -787,27 +754,23 @@ def content_type_for(cls, model): @classmethod def _needs_templates(cls): - ensure_completely_loaded() - # helper which can be used to ensure that either register_regions # or register_templates has been executed before proceeding - if not hasattr(cls, 'template'): + if not hasattr(cls, "template"): raise ImproperlyConfigured( - 'You need to register at least one' - ' template or one region on %s.' % cls.__name__) + "You need to register at least one" + " template or one region on %s." % cls.__name__ + ) @classmethod def _needs_content_types(cls): - ensure_completely_loaded() - # Check whether any content types have been created for this base # class - if ( - not hasattr(cls, '_feincms_content_types') - or not cls._feincms_content_types): + if not getattr(cls, "_feincms_content_types", None): raise ImproperlyConfigured( - 'You need to create at least one' - ' content type for the %s model.' % cls.__name__) + "You need to create at least one" + " content type for the %s model." % cls.__name__ + ) def copy_content_from(self, obj): """ @@ -818,8 +781,7 @@ def copy_content_from(self, obj): for cls in self._feincms_content_types: for content in cls.objects.filter(parent=obj): - new = copy_model_instance( - content, exclude=('id', 'parent')) + new = copy_model_instance(content, exclude=("id", "parent")) new.parent = self new.save() @@ -836,17 +798,20 @@ def replace_content_with(self, obj): self.copy_content_from(obj) @classmethod - def register_with_reversion(cls): + def register_with_reversion(cls, **kwargs): try: - import reversion + from reversion.revisions import register except ImportError: - raise EnvironmentError("django-reversion is not installed") + try: + from reversion import register + except ImportError: + raise OSError("django-reversion is not installed") follow = [] for content_type in cls._feincms_content_types: - follow.append('%s_set' % content_type.__name__.lower()) - reversion.register(content_type) - reversion.register(cls, follow=follow) + follow.append("%s_set" % content_type.__name__.lower()) + register(content_type, **kwargs) + register(cls, follow=follow, **kwargs) return Base diff --git a/feincms/module/blog/__init__.py b/feincms/module/blog/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/feincms/module/blog/admin.py b/feincms/module/blog/admin.py deleted file mode 100644 index 6a399e57c..000000000 --- a/feincms/module/blog/admin.py +++ /dev/null @@ -1,6 +0,0 @@ -from django.contrib import admin - -from feincms.module.blog.models import Entry, EntryAdmin - - -admin.site.register(Entry, EntryAdmin) diff --git a/feincms/module/blog/extensions/__init__.py b/feincms/module/blog/extensions/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/feincms/module/blog/extensions/tags.py b/feincms/module/blog/extensions/tags.py deleted file mode 100644 index 45731f552..000000000 --- a/feincms/module/blog/extensions/tags.py +++ /dev/null @@ -1,25 +0,0 @@ -""" -Simple tagging support using ``django-tagging``. -""" - -from __future__ import absolute_import, unicode_literals - -from django.utils.translation import ugettext_lazy as _ - -from feincms import extensions - -import tagging -from tagging.fields import TagField - - -class Extension(extensions.Extension): - def handle_model(self): - self.model.add_to_class('tags', TagField(_('tags'))) - - # use another name for the tag descriptor See - # http://code.google.com/p/django-tagging/issues/detail?id=95 for the - # reason why - tagging.register(self.model, tag_descriptor_attr='etags') - - def handle_modeladmin(self, modeladmin): - modeladmin.add_extension_options('tags') diff --git a/feincms/module/blog/extensions/translations.py b/feincms/module/blog/extensions/translations.py deleted file mode 100644 index a63a03787..000000000 --- a/feincms/module/blog/extensions/translations.py +++ /dev/null @@ -1,77 +0,0 @@ -""" -This extension adds a language field to every blog entry. - -Blog entries in secondary languages can be said to be a translation of a -blog entry in the primary language (the first language in settings.LANGUAGES), -thereby enabling deeplinks between translated blog entries. -""" - -from __future__ import absolute_import, unicode_literals - -from django.conf import settings -from django.db import models -from django.utils.translation import ugettext_lazy as _ - -from feincms import extensions - - -class Extension(extensions.Extension): - def handle_model(self): - primary_language = settings.LANGUAGES[0][0] - - self.model.add_to_class( - 'language', - models.CharField( - _('language'), - max_length=10, - choices=settings.LANGUAGES, - ) - ) - self.model.add_to_class( - 'translation_of', - models.ForeignKey( - 'self', - blank=True, null=True, - verbose_name=_('translation of'), - related_name='translations', - limit_choices_to={'language': primary_language}, - help_text=_( - 'Leave this empty for entries in the primary language.'), - ) - ) - - def available_translations(self): - if self.language == primary_language: - return self.translations.all() - elif self.translation_of: - return [self.translation_of] + list( - self.translation_of.translations.exclude( - language=self.language)) - else: - return [] - - self.model.available_translations = available_translations - - def available_translations_admin(self): - translations = self.available_translations() - - return ', '.join( - '%s' % ( - page.id, - page.language.upper() - ) for page in translations - ) - - available_translations_admin.allow_tags = True - available_translations_admin.short_description =\ - _('available translations') - self.model.available_translations_admin = available_translations_admin - - def handle_modeladmin(self, modeladmin): - modeladmin.add_extension_options('language') - - modeladmin.list_display.extend(( - 'language', 'available_translations_admin')) - modeladmin.list_filter.extend(('language',)) - - modeladmin.raw_id_fields.append('translation_of') diff --git a/feincms/module/blog/models.py b/feincms/module/blog/models.py deleted file mode 100644 index 7367c00b3..000000000 --- a/feincms/module/blog/models.py +++ /dev/null @@ -1,82 +0,0 @@ -""" -This is more a proof-of-concept for your own :class:`feincms.module.Base` -subclasses than a polished or even sufficient blog module implementation. - -It does work, though. -""" - -from __future__ import absolute_import, unicode_literals - -from django.db import models -from django.db.models import signals -from django.utils import timezone -from django.utils.encoding import python_2_unicode_compatible -from django.utils.translation import ugettext_lazy as _ - -from feincms import settings -from feincms.admin import item_editor -from feincms.management.checker import check_database_schema -from feincms.models import Base - - -class EntryManager(models.Manager): - def published(self): - return self.filter( - published=True, - published_on__isnull=False, - published_on__lte=timezone.now(), - ) - - -@python_2_unicode_compatible -class Entry(Base): - published = models.BooleanField(_('published'), default=False) - title = models.CharField( - _('title'), max_length=100, - help_text=_('This is used for the generated navigation too.')) - slug = models.SlugField() - - published_on = models.DateTimeField( - _('published on'), blank=True, null=True, - help_text=_( - 'Will be set automatically once you tick the `published`' - ' checkbox above.')) - - class Meta: - get_latest_by = 'published_on' - ordering = ['-published_on'] - verbose_name = _('entry') - verbose_name_plural = _('entries') - - objects = EntryManager() - - def __str__(self): - return self.title - - def save(self, *args, **kwargs): - if self.published and not self.published_on: - self.published_on = timezone.now() - super(Entry, self).save(*args, **kwargs) - save.alters_data = True - - @models.permalink - def get_absolute_url(self): - return ('blog_entry_detail', (self.id,), {}) - - -if settings.FEINCMS_CHECK_DATABASE_SCHEMA: - signals.post_syncdb.connect( - check_database_schema(Entry, __name__), - weak=False) - - -class EntryAdmin(item_editor.ItemEditor): - date_hierarchy = 'published_on' - list_display = ['__str__', 'published', 'published_on'] - list_filter = ['published'] - search_fields = ['title', 'slug'] - prepopulated_fields = { - 'slug': ('title',), - } - - raw_id_fields = [] diff --git a/feincms/module/extensions/changedate.py b/feincms/module/extensions/changedate.py index 1af2e3632..286d144fb 100644 --- a/feincms/module/extensions/changedate.py +++ b/feincms/module/extensions/changedate.py @@ -1,72 +1,11 @@ -# ------------------------------------------------------------------------ -# coding=utf-8 -# ------------------------------------------------------------------------ -""" -Track the modification date for objects. -""" +# flake8: noqa -from __future__ import absolute_import, unicode_literals +import warnings -from email.utils import parsedate_tz, mktime_tz -from django.db import models -from django.db.models.signals import pre_save -from django.utils import timezone -from django.utils.translation import ugettext_lazy as _ - -from feincms import extensions - - -# ------------------------------------------------------------------------ -def pre_save_handler(sender, instance, **kwargs): - """ - Intercept attempts to save and insert the current date and time into - creation and modification date fields. - """ - now = timezone.now() - if instance.id is None: - instance.creation_date = now - instance.modification_date = now - - -# ------------------------------------------------------------------------ -def dt_to_utc_timestamp(dt): - from time import mktime - return int(mktime(dt.timetuple())) - - -class Extension(extensions.Extension): - def handle_model(self): - self.model.add_to_class('creation_date', models.DateTimeField( - _('creation date'), null=True, editable=False)) - self.model.add_to_class('modification_date', models.DateTimeField( - _('modification date'), null=True, editable=False)) - - if hasattr(self.model, 'cache_key_components'): - self.model.cache_key_components.append( - lambda page: page.modification_date and str( - dt_to_utc_timestamp(page.modification_date))) - - self.model.last_modified = lambda p: p.modification_date - - pre_save.connect(pre_save_handler, sender=self.model) - - -# ------------------------------------------------------------------------ -def last_modified_response_processor(page, request, response): - from django.utils.http import http_date - - # Don't include Last-Modified if we don't want to be cached - if "no-cache" in response.get('Cache-Control', ''): - return - - # If we already have a Last-Modified, take the later one - last_modified = dt_to_utc_timestamp(page.last_modified()) - if response.has_header('Last-Modified'): - last_modified = max( - last_modified, - mktime_tz(parsedate_tz(response['Last-Modified']))) - - response['Last-Modified'] = http_date(last_modified) - -# ------------------------------------------------------------------------ +warnings.warn( + "Import %(name)s from feincms.extensions.%(name)s" + % {"name": __name__.split(".")[-1]}, + DeprecationWarning, + stacklevel=2, +) diff --git a/feincms/module/extensions/ct_tracker.py b/feincms/module/extensions/ct_tracker.py index 2065a45c4..286d144fb 100644 --- a/feincms/module/extensions/ct_tracker.py +++ b/feincms/module/extensions/ct_tracker.py @@ -1,163 +1,11 @@ -# ------------------------------------------------------------------------ -# coding=utf-8 -# ------------------------------------------------------------------------ -# -# ct_tracker.py -# FeinCMS -# -# Created by Martin J. Laubach on 02.10.09. -# Copyright (c) 2009 Martin J. Laubach. All rights reserved. -# Updated in 2011 by Matthias Kestenholz for the 1.3 release. -# -# ------------------------------------------------------------------------ +# flake8: noqa -""" -Track the content types for pages. Instead of gathering the content -types present in each page at run time, save the current state at -saving time, thus saving at least one DB query on page delivery. -""" +import warnings -from __future__ import absolute_import, unicode_literals -from django.contrib.contenttypes.models import ContentType -from django.db.models.signals import class_prepared, post_save, pre_save -from django.utils.translation import ugettext_lazy as _ - -from feincms import extensions -from feincms.contrib.fields import JSONField -from feincms.models import ContentProxy - - -INVENTORY_VERSION = 1 -_translation_map_cache = {} - - -# ------------------------------------------------------------------------ -class TrackerContentProxy(ContentProxy): - def _fetch_content_type_counts(self): - """ - If an object with an empty _ct_inventory is encountered, compute all - the content types currently used on that object and save the list in - the object itself. Further requests for that object can then access - that information and find out which content types are used without - resorting to multiple selects on different ct tables. - - It is therefore important that even an "empty" object does not have an - empty _ct_inventory. - """ - - if 'counts' not in self._cache: - if (self.item._ct_inventory - and self.item._ct_inventory.get('_version_', -1) - == INVENTORY_VERSION): - - try: - self._cache['counts'] = self._from_inventory( - self.item._ct_inventory) - except KeyError: - # It's possible that the inventory does not fit together - # with the current models anymore, f.e. because a content - # type has been removed. - pass - - if 'counts' not in self._cache: - super(TrackerContentProxy, self)._fetch_content_type_counts() - - self.item._ct_inventory = self._to_inventory( - self._cache['counts']) - - if hasattr(self.item, 'invalidate_cache'): - self.item.invalidate_cache() - self.item.__class__.objects.filter(id=self.item.id).update( - _ct_inventory=self.item._ct_inventory) - - # Run post save handler by hand - if hasattr(self.item, 'get_descendants'): - self.item.get_descendants(include_self=False).update( - _ct_inventory=None) - return self._cache['counts'] - - def _translation_map(self): - cls = self.item.__class__ - if cls not in _translation_map_cache: - # Prime translation map and cache it in the class. This needs to be - # done late as opposed to at class definition time as not all - # information is ready, especially when we are doing a "syncdb" the - # ContentType table does not yet exist - map = {} - for idx, fct in enumerate(self.item._feincms_content_types): - dct = ContentType.objects.get_for_model(fct) - - # Rely on non-negative primary keys - map[-dct.id] = idx # From-inventory map - map[idx] = dct.id # To-inventory map - - _translation_map_cache[cls] = map - return _translation_map_cache[cls] - - def _from_inventory(self, inventory): - """ - Transforms the inventory from Django's content types to FeinCMS's - ContentProxy counts format. - """ - - map = self._translation_map() - - return dict((region, [ - (pk, map[-ct]) for pk, ct in items - ]) for region, items in inventory.items() if region != '_version_') - - def _to_inventory(self, counts): - map = self._translation_map() - - inventory = dict( - ( - region, - [(pk, map[ct]) for pk, ct in items], - ) for region, items in counts.items() - ) - inventory['_version_'] = INVENTORY_VERSION - return inventory - - -# ------------------------------------------------------------------------ -def class_prepared_handler(sender, **kwargs): - # It might happen under rare circumstances that not all model classes - # are fully loaded and initialized when the translation map is accessed. - # This leads to (lots of) crashes on the server. Better be safe and - # kill the translation map when any class_prepared signal is received. - global _translation_map_cache - _translation_map_cache = {} -class_prepared.connect(class_prepared_handler) - - -# ------------------------------------------------------------------------ -def tree_post_save_handler(sender, instance, **kwargs): - """ - Clobber the _ct_inventory attribute of this object and all sub-objects - on save. - """ - # TODO: Does not find everything it should when ContentProxy content - # inheritance has been customized. - instance.get_descendants(include_self=True).update(_ct_inventory=None) - - -# ------------------------------------------------------------------------ -def single_pre_save_handler(sender, instance, **kwargs): - """Clobber the _ct_inventory attribute of this object""" - - instance._ct_inventory = None - - -# ------------------------------------------------------------------------ -class Extension(extensions.Extension): - def handle_model(self): - self.model.add_to_class('_ct_inventory', JSONField( - _('content types'), editable=False, blank=True, null=True)) - self.model.content_proxy_class = TrackerContentProxy - - pre_save.connect(single_pre_save_handler, sender=self.model) - if hasattr(self.model, 'get_descendants'): - post_save.connect(tree_post_save_handler, sender=self.model) - -# ------------------------------------------------------------------------ +warnings.warn( + "Import %(name)s from feincms.extensions.%(name)s" + % {"name": __name__.split(".")[-1]}, + DeprecationWarning, + stacklevel=2, +) diff --git a/feincms/module/extensions/datepublisher.py b/feincms/module/extensions/datepublisher.py index c6bb92791..286d144fb 100644 --- a/feincms/module/extensions/datepublisher.py +++ b/feincms/module/extensions/datepublisher.py @@ -1,143 +1,11 @@ -""" -Allows setting a date range for when the page is active. Modifies the active() -manager method so that only pages inside the given range are used in the -default views and the template tags. +# flake8: noqa -Depends on the page class having a "active_filters" list that will be used by -the page's manager to determine which entries are to be considered active. -""" -# ------------------------------------------------------------------------ +import warnings -from __future__ import absolute_import, unicode_literals -from datetime import datetime - -from django.db import models -from django.db.models import Q -from django.utils import timezone -from django.utils.cache import patch_response_headers -from django.utils.translation import ugettext_lazy as _ - -from feincms import extensions - - -# ------------------------------------------------------------------------ -def format_date(d, if_none=''): - """ - Format a date in a nice human readable way: Omit the year if it's the - current year. Also return a default value if no date is passed in. - """ - - if d is None: - return if_none - - now = timezone.now() - fmt = (d.year == now.year) and '%d.%m' or '%d.%m.%Y' - return d.strftime(fmt) - - -def latest_children(self): - return self.get_children().order_by('-publication_date') - - -# ------------------------------------------------------------------------ -def granular_now(n=None): - """ - A datetime.now look-alike that returns times rounded to a five minute - boundary. This helps the backend database to optimize/reuse/cache its - queries by not creating a brand new query each time. - - Also useful if you are using johnny-cache or a similar queryset cache. - """ - if n is None: - n = timezone.now() - return timezone.make_aware( - datetime(n.year, n.month, n.day, n.hour, (n.minute // 5) * 5), - n.tzinfo) - - -# ------------------------------------------------------------------------ -def datepublisher_response_processor(page, request, response): - """ - This response processor is automatically added when the datepublisher - extension is registered. It sets the response headers to match with - the publication end date of the page so that upstream caches and - the django caching middleware know when to expunge the copy. - """ - expires = page.publication_end_date - if expires is not None: - now = datetime.now() - delta = expires - now - - try: - delta = int(delta.days * 86400 + delta.seconds) - except Exception: - # This happens once every four years (or so) - delta = int(delta.days * 86400 + delta.seconds - 7200) - - patch_response_headers(response, delta) - - -# ------------------------------------------------------------------------ -class Extension(extensions.Extension): - def handle_model(self): - self.model.add_to_class( - 'publication_date', - models.DateTimeField(_('publication date'), default=granular_now)) - self.model.add_to_class( - 'publication_end_date', - models.DateTimeField( - _('publication end date'), - blank=True, null=True, - help_text=_( - 'Leave empty if the entry should stay active forever.'))) - self.model.add_to_class('latest_children', latest_children) - - # Patch in rounding the pub and pub_end dates on save - orig_save = self.model.save - - def granular_save(obj, *args, **kwargs): - if obj.publication_date: - obj.publication_date = granular_now(obj.publication_date) - if obj.publication_end_date: - obj.publication_end_date = granular_now( - obj.publication_end_date) - orig_save(obj, *args, **kwargs) - self.model.save = granular_save - - # Append publication date active check - if hasattr(self.model._default_manager, 'add_to_active_filters'): - self.model._default_manager.add_to_active_filters( - Q(publication_date__lte=granular_now) & - (Q(publication_end_date__isnull=True) | - Q(publication_end_date__gt=granular_now)), - key='datepublisher', - ) - - # Processor to patch up response headers for expiry date - self.model.register_response_processor( - datepublisher_response_processor) - - def handle_modeladmin(self, modeladmin): - def datepublisher_admin(self, obj): - return '%s – %s' % ( - format_date(obj.publication_date), - format_date(obj.publication_end_date, '∞'), - ) - datepublisher_admin.allow_tags = True - datepublisher_admin.short_description = _('visible from - to') - - modeladmin.__class__.datepublisher_admin = datepublisher_admin - - try: - pos = modeladmin.list_display.index('is_visible_admin') - except ValueError: - pos = len(modeladmin.list_display) - - modeladmin.list_display.insert(pos + 1, 'datepublisher_admin') - - modeladmin.add_extension_options(_('Date-based publishing'), { - 'fields': ['publication_date', 'publication_end_date'], - }) - -# ------------------------------------------------------------------------ +warnings.warn( + "Import %(name)s from feincms.extensions.%(name)s" + % {"name": __name__.split(".")[-1]}, + DeprecationWarning, + stacklevel=2, +) diff --git a/feincms/module/extensions/featured.py b/feincms/module/extensions/featured.py index 6930a58a7..286d144fb 100644 --- a/feincms/module/extensions/featured.py +++ b/feincms/module/extensions/featured.py @@ -1,24 +1,11 @@ -""" -Add a "featured" field to objects so admins can better direct top content. -""" +# flake8: noqa -from __future__ import absolute_import, unicode_literals +import warnings -from django.db import models -from django.utils.translation import ugettext_lazy as _ -from feincms import extensions - - -class Extension(extensions.Extension): - def handle_model(self): - self.model.add_to_class('featured', models.BooleanField(_('featured'))) - - if hasattr(self.model, 'cache_key_components'): - self.model.cache_key_components.append(lambda page: page.featured) - - def handle_modeladmin(self, modeladmin): - modeladmin.add_extension_options(_('Featured'), { - 'fields': ('featured',), - 'classes': ('collapse',), - }) +warnings.warn( + "Import %(name)s from feincms.extensions.%(name)s" + % {"name": __name__.split(".")[-1]}, + DeprecationWarning, + stacklevel=2, +) diff --git a/feincms/module/extensions/seo.py b/feincms/module/extensions/seo.py index f71d9f5f3..286d144fb 100644 --- a/feincms/module/extensions/seo.py +++ b/feincms/module/extensions/seo.py @@ -1,35 +1,11 @@ -""" -Add a keyword and a description field which are helpful for SEO optimization. -""" +# flake8: noqa -from __future__ import absolute_import, unicode_literals +import warnings -from django.db import models -from django.utils.translation import ugettext_lazy as _ -from feincms import extensions - - -class Extension(extensions.Extension): - def handle_model(self): - self.model.add_to_class('meta_keywords', models.TextField( - _('meta keywords'), - blank=True, - help_text=_('Keywords are ignored by most search engines.'))) - self.model.add_to_class('meta_description', models.TextField( - _('meta description'), - blank=True, - help_text=_('This text is displayed on the search results page. ' - 'It is however not used for the SEO ranking. ' - 'Text longer than 140 characters is truncated.'))) - - def handle_modeladmin(self, modeladmin): - modeladmin.extend_list( - 'search_fields', - ['meta_keywords', 'meta_description'], - ) - - modeladmin.add_extension_options(_('Search engine optimization'), { - 'fields': ('meta_keywords', 'meta_description'), - 'classes': ('collapse',), - }) +warnings.warn( + "Import %(name)s from feincms.extensions.%(name)s" + % {"name": __name__.split(".")[-1]}, + DeprecationWarning, + stacklevel=2, +) diff --git a/feincms/module/extensions/translations.py b/feincms/module/extensions/translations.py index 10065b685..286d144fb 100644 --- a/feincms/module/extensions/translations.py +++ b/feincms/module/extensions/translations.py @@ -1,268 +1,11 @@ -# ------------------------------------------------------------------------ -# coding=utf-8 -# ------------------------------------------------------------------------ +# flake8: noqa -""" -This extension adds a language field to every page. When calling the request -processors the page's language is activated. -Pages in secondary languages can be said to be a translation of a page in the -primary language (the first language in settings.LANGUAGES), thereby enabling -deeplinks between translated pages. +import warnings -It is recommended to activate -:class:`django.middleware.locale.LocaleMiddleware` so that the correct language -will be activated per user or session even for non-FeinCMS managed views such -as Django's administration tool. -""" -from __future__ import absolute_import, unicode_literals - -# ------------------------------------------------------------------------ -import logging - -from django.conf import settings as django_settings -from django.db import models -from django.http import HttpResponseRedirect -from django.utils import translation -from django.utils.translation import ugettext_lazy as _ - -from feincms import extensions, settings -from feincms.translations import is_primary_language -from feincms._internal import monkeypatch_method, monkeypatch_property - - -# ------------------------------------------------------------------------ -logger = logging.getLogger(__name__) - - -# ------------------------------------------------------------------------ -def user_has_language_set(request): - """ - Determine whether the user has explicitely set a language earlier on. - This is taken later on as an indication that we should not mess with the - site's language settings, after all, the user's decision is what counts. - """ - if (hasattr(request, 'session') - and request.session.get('django_language') is not None): - return True - if django_settings.LANGUAGE_COOKIE_NAME in request.COOKIES: - return True - return False - - -# ------------------------------------------------------------------------ -def translation_set_language(request, select_language): - """ - Set and activate a language, if that language is available. - """ - if translation.check_for_language(select_language): - fallback = False - else: - # The page is in a language that Django has no messages for. - # We display anyhow, but fall back to primary language for - # other messages and other applications. It is *highly* recommended to - # create a new django.po for the language instead of - # using this behaviour. - select_language = django_settings.LANGUAGES[0][0] - fallback = True - - translation.activate(select_language) - request.LANGUAGE_CODE = translation.get_language() - - if hasattr(request, 'session'): - # User has a session, then set this language there - if select_language != request.session.get('django_language'): - request.session['django_language'] = select_language - elif request.method == 'GET' and not fallback: - # No session is active. We need to set a cookie for the language - # so that it persists when users change their location to somewhere - # not under the control of the CMS. - # Only do this when request method is GET (mainly, do not abort - # POST requests) - response = HttpResponseRedirect(request.get_full_path()) - response.set_cookie( - str(django_settings.LANGUAGE_COOKIE_NAME), select_language) - return response - - -# ------------------------------------------------------------------------ -def translations_request_processor_explicit(page, request): - # If this page is just a redirect, don't do any language specific setup - if page.redirect_to: - return - - # Until further notice, the user might be wanting to switch to the - # page's language... - desired_language = page.language - - # ...except if the user explicitely wants to switch language - if 'set_language' in request.GET: - desired_language = request.GET['set_language'] - # ...or the user already has explicitely set a language, bail out and - # don't change it for them behind their back - elif user_has_language_set(request): - return - - return translation_set_language(request, desired_language) - - -# ------------------------------------------------------------------------ -def translations_request_processor_standard(page, request): - # If this page is just a redirect, don't do any language specific setup - if getattr(page, 'redirect_to', None): - return - - if page.language == translation.get_language(): - return - - return translation_set_language(request, page.language) - - -# ------------------------------------------------------------------------ -def get_current_language_code(request): - language_code = getattr(request, 'LANGUAGE_CODE', None) - if language_code is None: - logger.warning( - "Could not access request.LANGUAGE_CODE. Is 'django.middleware." - "locale.LocaleMiddleware' in MIDDLEWARE_CLASSES?") - return language_code - - -# ------------------------------------------------------------------------ -class Extension(extensions.Extension): - - def handle_model(self): - cls = self.model - - cls.add_to_class( - 'language', - models.CharField( - _('language'), - max_length=10, - choices=django_settings.LANGUAGES, - default=django_settings.LANGUAGES[0][0])) - cls.add_to_class( - 'translation_of', - models.ForeignKey( - 'self', - blank=True, null=True, verbose_name=_('translation of'), - related_name='translations', - limit_choices_to={'language': django_settings.LANGUAGES[0][0]}, - help_text=_( - 'Leave this empty for entries in the primary language.'), - ) - ) - - if hasattr(cls, 'register_request_processor'): - if settings.FEINCMS_TRANSLATION_POLICY == "EXPLICIT": - cls.register_request_processor( - translations_request_processor_explicit, - key='translations') - else: # STANDARD - cls.register_request_processor( - translations_request_processor_standard, - key='translations') - - if hasattr(cls, 'get_redirect_to_target'): - original_get_redirect_to_target = cls.get_redirect_to_target - - @monkeypatch_method(cls) - def get_redirect_to_target(self, request): - """ - Find an acceptable redirect target. If this is a local link, - then try to find the page this redirect references and - translate it according to the user's language. This way, one - can easily implement a localized "/"-url to welcome page - redirection. - """ - target = original_get_redirect_to_target(self, request) - if target and target.find('//') == -1: - # Not an offsite link http://bla/blubb - try: - page = cls.objects.page_for_path(target) - page = page.get_translation( - get_current_language_code(request)) - target = page.get_absolute_url() - except cls.DoesNotExist: - pass - return target - - @monkeypatch_method(cls) - def available_translations(self): - if not self.id: # New, unsaved pages have no translations - return [] - if is_primary_language(self.language): - return self.translations.all() - elif self.translation_of: - # reuse prefetched queryset, do not filter it - res = [t for t in list(self.translation_of.translations.all()) - if t.language != self.language] - res.insert(0, self.translation_of) - return res - else: - return [] - - @monkeypatch_method(cls) - def get_original_translation(self, *args, **kwargs): - if is_primary_language(self.language): - return self - if self.translation_of: - return self.translation_of - raise self.DoesNotExist - - @monkeypatch_property(cls) - def original_translation(self): - return self.get_original_translation() - - @monkeypatch_method(cls) - def get_translation(self, language): - return self.original_translation.translations.get( - language=language) - - def handle_modeladmin(self, modeladmin): - - extensions.prefetch_modeladmin_get_queryset( - modeladmin, 'translation_of__translations', 'translations') - - def available_translations_admin(self, page): - translations = dict( - (p.language, p.id) for p in page.available_translations()) - - links = [] - - for key, title in django_settings.LANGUAGES: - if key == page.language: - continue - - if key in translations: - links.append('%s' % ( - translations[key], _('Edit translation'), key.upper())) - else: - links.append( - '%s' % ( - page.id, - key, - _('Create translation'), - key.upper() - ) - ) - - return ' | '.join(links) - - available_translations_admin.allow_tags = True - available_translations_admin.short_description = _('translations') - modeladmin.__class__.available_translations_admin =\ - available_translations_admin - - if hasattr(modeladmin, 'add_extension_options'): - modeladmin.add_extension_options('language', 'translation_of') - - modeladmin.extend_list( - 'list_display', - ['language', 'available_translations_admin'], - ) - modeladmin.extend_list('list_filter', ['language']) - modeladmin.extend_list('raw_id_fields', ['translation_of']) - -# ------------------------------------------------------------------------ +warnings.warn( + "Import %(name)s from feincms.extensions.%(name)s" + % {"name": __name__.split(".")[-1]}, + DeprecationWarning, + stacklevel=2, +) diff --git a/feincms/module/medialibrary/__init__.py b/feincms/module/medialibrary/__init__.py index fc7e02a00..bb74687b2 100644 --- a/feincms/module/medialibrary/__init__.py +++ b/feincms/module/medialibrary/__init__.py @@ -1,12 +1,11 @@ # ------------------------------------------------------------------------ -# coding=utf-8 # ------------------------------------------------------------------------ -from __future__ import absolute_import import logging + # ------------------------------------------------------------------------ -logger = logging.getLogger('feincms.medialibrary') +logger = logging.getLogger("feincms.medialibrary") # ------------------------------------------------------------------------ diff --git a/feincms/module/medialibrary/admin.py b/feincms/module/medialibrary/admin.py index 922450af9..b9fa8f33f 100644 --- a/feincms/module/medialibrary/admin.py +++ b/feincms/module/medialibrary/admin.py @@ -1,13 +1,12 @@ # ------------------------------------------------------------------------ -# coding=utf-8 # ------------------------------------------------------------------------ -from __future__ import absolute_import, unicode_literals from django.contrib import admin -from .models import Category, MediaFile from .modeladmins import CategoryAdmin, MediaFileAdmin +from .models import Category, MediaFile + # ------------------------------------------------------------------------ admin.site.register(Category, CategoryAdmin) diff --git a/feincms/module/medialibrary/contents.py b/feincms/module/medialibrary/contents.py new file mode 100644 index 000000000..63a2153fc --- /dev/null +++ b/feincms/module/medialibrary/contents.py @@ -0,0 +1,76 @@ +from django.contrib import admin +from django.core.exceptions import ImproperlyConfigured +from django.db import models +from django.utils.translation import gettext_lazy as _ + +from feincms.admin.item_editor import FeinCMSInline +from feincms.module.medialibrary.fields import ContentWithMediaFile +from feincms.utils.tuple import AutoRenderTuple + + +class MediaFileContentInline(FeinCMSInline): + raw_id_fields = ("mediafile",) + radio_fields = {"type": admin.VERTICAL} + + +class MediaFileContent(ContentWithMediaFile): + """ + Rehashed, backwards-incompatible media file content which does not contain + the problems from v1 anymore. + + Create a media file content as follows:: + + from feincms.content.medialibrary.models import MediaFileContent + Page.create_content_type(MediaFileContent, TYPE_CHOICES=( + ('default', _('Default')), + ('lightbox', _('Lightbox')), + ('whatever', _('Whatever')), + )) + + For a media file of type 'image' and type 'lightbox', the following + templates are tried in order: + + * content/mediafile/image_lightbox.html + * content/mediafile/image.html + * content/mediafile/lightbox.html + * content/mediafile/default.html + + The context contains ``content`` and ``request`` (if available). + """ + + feincms_item_editor_inline = MediaFileContentInline + + class Meta: + abstract = True + verbose_name = _("media file") + verbose_name_plural = _("media files") + + @classmethod + def initialize_type(cls, TYPE_CHOICES=None): + if TYPE_CHOICES is None: + raise ImproperlyConfigured( + "You have to set TYPE_CHOICES when creating a %s" % cls.__name__ + ) + + cls.add_to_class( + "type", + models.CharField( + _("type"), + max_length=20, + choices=TYPE_CHOICES, + default=TYPE_CHOICES[0][0], + ), + ) + + def render(self, **kwargs): + return AutoRenderTuple( + ( + [ + f"content/mediafile/{self.mediafile.type}_{self.type}.html", + "content/mediafile/%s.html" % self.mediafile.type, + "content/mediafile/%s.html" % self.type, + "content/mediafile/default.html", + ], + {"content": self}, + ) + ) diff --git a/feincms/module/medialibrary/fields.py b/feincms/module/medialibrary/fields.py index f933f0dbb..0910ca540 100644 --- a/feincms/module/medialibrary/fields.py +++ b/feincms/module/medialibrary/fields.py @@ -1,24 +1,20 @@ # ------------------------------------------------------------------------ -# coding=utf-8 # ------------------------------------------------------------------------ -from __future__ import absolute_import, unicode_literals -from django.contrib.admin.widgets import AdminFileWidget -from django.contrib.admin.widgets import ForeignKeyRawIdWidget +from django.contrib.admin.widgets import AdminFileWidget, ForeignKeyRawIdWidget from django.db import models -from django.utils import six -from django.utils.html import escape -from django.utils.safestring import mark_safe -from django.utils.translation import ugettext_lazy as _ +from django.utils.html import escape, mark_safe +from django.utils.translation import gettext_lazy as _ from feincms.admin.item_editor import FeinCMSInline from feincms.utils import shorten_string + from .models import MediaFile from .thumbnail import admin_thumbnail -__all__ = ('MediaFileForeignKey', 'ContentWithMediaFile') +__all__ = ("MediaFileForeignKey", "ContentWithMediaFile") # ------------------------------------------------------------------------ @@ -26,23 +22,26 @@ class MediaFileForeignKeyRawIdWidget(ForeignKeyRawIdWidget): def __init__(self, original): self.__dict__ = original.__dict__ - def label_for_value(self, value): - key = self.rel.get_related_field().name + def label_and_url_for_value(self, value): + label, url = super().label_and_url_for_value(value) + key = "pk" try: - obj = self.rel.to._default_manager.using(self.db).get( - **{key: value}) - label = [' %s' % escape( - shorten_string(six.text_type(obj)))] + obj = ( + self.rel.model._default_manager.using(self.db) + .filter(**{key: value}) + .first() + ) + label = [" %s" % escape(shorten_string(str(obj)))] image = admin_thumbnail(obj) if image: label.append( - '
    ' % image) + '
    ' % image + ) - return ''.join(label) - except (ValueError, self.rel.to.DoesNotExist): - return '' + return mark_safe("".join(label)), url + except (ValueError, self.rel.model.DoesNotExist): + return label, url class MediaFileForeignKey(models.ForeignKey): @@ -51,19 +50,28 @@ class MediaFileForeignKey(models.ForeignKey): adds a thumbnail of media files if the media file foreign key is shown using ``raw_id_fields``. """ + + def __init__(self, *args, **kwargs): + if not args and "to" not in kwargs: + args = (MediaFile,) + super().__init__(*args, **kwargs) + def formfield(self, **kwargs): - if 'widget' in kwargs and isinstance( - kwargs['widget'], ForeignKeyRawIdWidget): - kwargs['widget'] = MediaFileForeignKeyRawIdWidget(kwargs['widget']) - return super(MediaFileForeignKey, self).formfield(**kwargs) + if "widget" in kwargs and isinstance(kwargs["widget"], ForeignKeyRawIdWidget): + kwargs["widget"] = MediaFileForeignKeyRawIdWidget(kwargs["widget"]) + return super().formfield(**kwargs) class ContentWithMediaFile(models.Model): class feincms_item_editor_inline(FeinCMSInline): - raw_id_fields = ('mediafile',) + raw_id_fields = ("mediafile",) mediafile = MediaFileForeignKey( - MediaFile, verbose_name=_('media file'), related_name='+') + MediaFile, + verbose_name=_("media file"), + related_name="+", + on_delete=models.PROTECT, + ) class Meta: abstract = True @@ -75,26 +83,20 @@ class AdminFileWithPreviewWidget(AdminFileWidget): Simple AdminFileWidget, but detects if the file is an image and tries to render a small thumbnail besides the input field. """ - def render(self, name, value, attrs=None): - r = super(AdminFileWithPreviewWidget, self).render( - name, value, attrs=attrs) - if value and getattr(value, 'instance', None): + def render(self, name, value, attrs=None, *args, **kwargs): + r = super().render(name, value, attrs=attrs, *args, **kwargs) + + if value and getattr(value, "instance", None): image = admin_thumbnail(value.instance) if image: - r = mark_safe(( - '' % image) + r) + r = mark_safe( + ( + '" % image + ) + + r + ) return r - -# ------------------------------------------------------------------------ - -try: - from south.modelsinspector import add_introspection_rules - add_introspection_rules( - rules=[((MediaFileForeignKey,), [], {},)], - patterns=["^feincms\.module\.medialibrary\.fields"]) -except ImportError: - pass diff --git a/feincms/module/medialibrary/forms.py b/feincms/module/medialibrary/forms.py index fc598f6c5..c2f094322 100644 --- a/feincms/module/medialibrary/forms.py +++ b/feincms/module/medialibrary/forms.py @@ -1,58 +1,60 @@ # ------------------------------------------------------------------------ -# coding=utf-8 # ------------------------------------------------------------------------ -from __future__ import absolute_import, unicode_literals import os from django import forms -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import gettext_lazy as _ from feincms import settings from . import logger -from .models import Category, MediaFile from .fields import AdminFileWithPreviewWidget +from .models import Category, MediaFile # ------------------------------------------------------------------------ class MediaCategoryAdminForm(forms.ModelForm): class Meta: model = Category + fields = "__all__" def clean_parent(self): - data = self.cleaned_data['parent'] + data = self.cleaned_data["parent"] if data is not None and self.instance in data.path_list(): - raise forms.ValidationError( - _("This would create a loop in the hierarchy")) + raise forms.ValidationError(_("This would create a loop in the hierarchy")) return data def __init__(self, *args, **kwargs): - super(MediaCategoryAdminForm, self).__init__(*args, **kwargs) - self.fields['parent'].queryset =\ - self.fields['parent'].queryset.exclude(pk=self.instance.pk) + super().__init__(*args, **kwargs) + self.fields["parent"].queryset = self.fields["parent"].queryset.exclude( + pk=self.instance.pk + ) # ------------------------------------------------------------------------ class MediaFileAdminForm(forms.ModelForm): class Meta: model = MediaFile - widgets = {'file': AdminFileWithPreviewWidget} + widgets = {"file": AdminFileWithPreviewWidget} + fields = "__all__" def __init__(self, *args, **kwargs): - super(MediaFileAdminForm, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) if settings.FEINCMS_MEDIAFILE_OVERWRITE and self.instance.id: field = self.instance.file.field - if not hasattr(field, '_feincms_generate_filename_patched'): + if not hasattr(field, "_feincms_generate_filename_patched"): original_generate = field.generate_filename def _gen_fname(instance, filename): - if instance.id and hasattr(instance, 'original_name'): - logger.info("Overwriting file %s with new data" % ( - instance.original_name)) + if instance.id and hasattr(instance, "original_name"): + logger.info( + "Overwriting file %s with new data" + % (instance.original_name) + ) instance.file.storage.delete(instance.original_name) return instance.original_name @@ -63,18 +65,21 @@ def _gen_fname(instance, filename): def clean_file(self): if settings.FEINCMS_MEDIAFILE_OVERWRITE and self.instance.id: - new_base, new_ext = os.path.splitext( - self.cleaned_data['file'].name) + new_base, new_ext = os.path.splitext(self.cleaned_data["file"].name) old_base, old_ext = os.path.splitext(self.instance.file.name) if new_ext.lower() != old_ext.lower(): - raise forms.ValidationError(_( - "Cannot overwrite with different file type (attempt to" - " overwrite a %(old_ext)s with a %(new_ext)s)" - ) % {'old_ext': old_ext, 'new_ext': new_ext}) + raise forms.ValidationError( + _( + "Cannot overwrite with different file type (attempt to" + " overwrite a %(old_ext)s with a %(new_ext)s)" + ) + % {"old_ext": old_ext, "new_ext": new_ext} + ) self.instance.original_name = self.instance.file.name - return self.cleaned_data['file'] + return self.cleaned_data["file"] + # ------------------------------------------------------------------------ diff --git a/feincms/module/medialibrary/modeladmins.py b/feincms/module/medialibrary/modeladmins.py index 7c8c390a0..721b97c04 100644 --- a/feincms/module/medialibrary/modeladmins.py +++ b/feincms/module/medialibrary/modeladmins.py @@ -1,33 +1,30 @@ # ------------------------------------------------------------------------ -# coding=utf-8 # ------------------------------------------------------------------------ -from __future__ import absolute_import, unicode_literals import os from django import forms from django.conf import settings as django_settings -from django.contrib import admin -from django.contrib import messages +from django.contrib import admin, messages +from django.contrib.admin.helpers import ACTION_CHECKBOX_NAME from django.contrib.auth.decorators import permission_required -from django.contrib.sites.models import Site +from django.contrib.sites.shortcuts import get_current_site from django.core.files.images import get_image_dimensions -from django.core.urlresolvers import reverse from django.http import HttpResponseRedirect -from django.shortcuts import render_to_response -from django.template.context import RequestContext +from django.shortcuts import render from django.template.defaultfilters import filesizeformat +from django.urls import reverse from django.utils.safestring import mark_safe -from django.utils.translation import ungettext, ugettext_lazy as _ +from django.utils.translation import gettext_lazy as _, ngettext from django.views.decorators.csrf import csrf_protect from feincms.extensions import ExtensionModelAdmin from feincms.translations import admin_translationinline, lookup_translations from feincms.utils import shorten_string -from .models import Category, MediaFileTranslation from .forms import MediaCategoryAdminForm, MediaFileAdminForm +from .models import Category, MediaFileTranslation from .thumbnail import admin_thumbnail from .zip import import_zipfile @@ -35,60 +32,59 @@ # ----------------------------------------------------------------------- class CategoryAdmin(admin.ModelAdmin): form = MediaCategoryAdminForm - list_display = ['path'] - list_filter = ['parent'] + list_display = ["path"] + list_filter = ["parent"] list_per_page = 25 - search_fields = ['title'] - prepopulated_fields = {'slug': ('title',)} + search_fields = ["title"] + prepopulated_fields = {"slug": ("title",)} # ------------------------------------------------------------------------ +@admin.action(description=_("Add selected media files to category")) def assign_category(modeladmin, request, queryset): class AddCategoryForm(forms.Form): _selected_action = forms.CharField(widget=forms.MultipleHiddenInput) category = forms.ModelChoiceField(Category.objects.all()) form = None - if 'apply' in request.POST: + if "apply" in request.POST: form = AddCategoryForm(request.POST) if form.is_valid(): - category = form.cleaned_data['category'] + category = form.cleaned_data["category"] count = 0 for mediafile in queryset: category.mediafile_set.add(mediafile) count += 1 - message = ungettext( - 'Successfully added %(count)d media file to %(category)s.', - 'Successfully added %(count)d media files to %(category)s.', - count) % {'count': count, 'category': category} + message = ngettext( + "Successfully added %(count)d media file to %(category)s.", + "Successfully added %(count)d media files to %(category)s.", + count, + ) % {"count": count, "category": category} modeladmin.message_user(request, message) return HttpResponseRedirect(request.get_full_path()) - if 'cancel' in request.POST: + if "cancel" in request.POST: return HttpResponseRedirect(request.get_full_path()) if not form: - form = AddCategoryForm(initial={ - '_selected_action': request.POST.getlist( - admin.ACTION_CHECKBOX_NAME), - }) - - return render_to_response('admin/medialibrary/add_to_category.html', { - 'mediafiles': queryset, - 'category_form': form, - 'opts': modeladmin.model._meta, - }, context_instance=RequestContext(request)) - + form = AddCategoryForm( + initial={"_selected_action": request.POST.getlist(ACTION_CHECKBOX_NAME)} + ) -assign_category.short_description = _('Add selected media files to category') + return render( + request, + "admin/medialibrary/add_to_category.html", + {"mediafiles": queryset, "category_form": form, "opts": modeladmin.model._meta}, + ) # ------------------------------------------------------------------------- +@admin.action(description=_("Export selected media files as zip file")) def save_as_zipfile(modeladmin, request, queryset): from .zip import export_zipfile - site = Site.objects.get_current() + site = get_current_site(request) try: zip_name = export_zipfile(site, queryset) messages.info(request, _("ZIP file exported as %s") % zip_name) @@ -96,12 +92,7 @@ def save_as_zipfile(modeladmin, request, queryset): messages.error(request, _("ZIP file export failed: %s") % str(e)) return - return HttpResponseRedirect( - os.path.join(django_settings.MEDIA_URL, zip_name)) - - -save_as_zipfile.short_description = _( - 'Export selected media files as zip file') + return HttpResponseRedirect(os.path.join(django_settings.MEDIA_URL, zip_name)) # ------------------------------------------------------------------------ @@ -109,67 +100,68 @@ class MediaFileAdmin(ExtensionModelAdmin): form = MediaFileAdminForm save_on_top = True - date_hierarchy = 'created' + date_hierarchy = "created" inlines = [admin_translationinline(MediaFileTranslation)] - list_display = [ - 'admin_thumbnail', '__str__', 'file_info', 'formatted_created'] - list_display_links = ['__str__'] - list_filter = ['type', 'categories'] + list_display = ["admin_thumbnail", "__str__", "file_info", "formatted_created"] + list_display_links = ["__str__"] + list_filter = ["type", "categories"] list_per_page = 25 - search_fields = ['copyright', 'file', 'translations__caption'] + search_fields = ["copyright", "file", "translations__caption"] filter_horizontal = ("categories",) actions = [assign_category, save_as_zipfile] def get_urls(self): - from django.conf.urls import patterns, url + from django.urls import path - urls = super(MediaFileAdmin, self).get_urls() - my_urls = patterns( - '', - url( - r'^mediafile-bulk-upload/$', + return [ + path( + "mediafile-bulk-upload/", self.admin_site.admin_view(MediaFileAdmin.bulk_upload), {}, - name='mediafile_bulk_upload', - ), - ) - - return my_urls + urls + name="mediafile_bulk_upload", + ) + ] + super().get_urls() def changelist_view(self, request, extra_context=None): if extra_context is None: extra_context = {} - extra_context['categories'] = Category.objects.order_by('title') - return super(MediaFileAdmin, self).changelist_view( - request, extra_context=extra_context) + extra_context["categories"] = Category.objects.order_by("title") + return super().changelist_view(request, extra_context=extra_context) + @admin.display(description=_("Preview")) def admin_thumbnail(self, obj): image = admin_thumbnail(obj) if image: - return mark_safe(""" + return mark_safe( + """ - """ % { - 'url': obj.file.url, - 'image': image} + """ + % {"url": obj.file.url, "image": image} ) - return '' - admin_thumbnail.short_description = _('Preview') - admin_thumbnail.allow_tags = True + return "" + @admin.display( + description=_("file size"), + ordering="file_size", + ) def formatted_file_size(self, obj): return filesizeformat(obj.file_size) - formatted_file_size.short_description = _("file size") - formatted_file_size.admin_order_field = 'file_size' + @admin.display( + description=_("created"), + ordering="created", + ) def formatted_created(self, obj): return obj.created.strftime("%Y-%m-%d") - formatted_created.short_description = _("created") - formatted_created.admin_order_field = 'created' + @admin.display( + description=_("file type"), + ordering="type", + ) def file_type(self, obj): t = obj.filetypes_dict[obj.type] - if obj.type == 'image': + if obj.type == "image": # get_image_dimensions is expensive / slow if the storage is not # local filesystem (indicated by availability the path property) try: @@ -177,16 +169,17 @@ def file_type(self, obj): except NotImplementedError: return t try: - d = get_image_dimensions(obj.file.file) + d = get_image_dimensions(obj.file.file, close=True) if d: t += " %d×%d" % (d[0], d[1]) - except (IOError, ValueError) as e: + except (OSError, TypeError, ValueError) as e: t += " (%s)" % e - return t - file_type.admin_order_field = 'type' - file_type.short_description = _('file type') - file_type.allow_tags = True + return mark_safe(t) + @admin.display( + description=_("file info"), + ordering="file", + ) def file_info(self, obj): """ Method for showing the file name in admin. @@ -195,48 +188,48 @@ def file_info(self, obj): the file name later on, this can be used to access the file name from JS, like for example a TinyMCE connector shim. """ - return ( - '' - ' %s
    %s, %s' - ) % ( - obj.id, - obj.file.name, - obj.id, - shorten_string(os.path.basename(obj.file.name), max_length=40), - self.file_type(obj), - self.formatted_file_size(obj), + return mark_safe( + ( + '' + " %s
    %s, %s" + ) + % ( + obj.id, + obj.file.name, + obj.id, + shorten_string(os.path.basename(obj.file.name), max_length=40), + self.file_type(obj), + self.formatted_file_size(obj), + ) ) - file_info.admin_order_field = 'file' - file_info.short_description = _('file info') - file_info.allow_tags = True @staticmethod @csrf_protect - @permission_required('medialibrary.add_mediafile') + @permission_required("medialibrary.add_mediafile") def bulk_upload(request): - if request.method == 'POST' and 'data' in request.FILES: + if request.method == "POST" and "data" in request.FILES: try: count = import_zipfile( - request.POST.get('category'), - request.POST.get('overwrite', False), - request.FILES['data']) + request.POST.get("category"), + request.POST.get("overwrite", False), + request.FILES["data"], + ) messages.info(request, _("%d files imported") % count) except Exception as e: messages.error(request, _("ZIP import failed: %s") % e) else: messages.error(request, _("No input file given")) - return HttpResponseRedirect( - reverse('admin:medialibrary_mediafile_changelist')) + return HttpResponseRedirect(reverse("admin:medialibrary_mediafile_changelist")) - def queryset(self, request): - return super(MediaFileAdmin, self).queryset(request).transform( - lookup_translations()) + def get_queryset(self, request): + return super().get_queryset(request).transform(lookup_translations()) def save_model(self, request, obj, form, change): - obj.purge_translation_cache() - return super(MediaFileAdmin, self).save_model( - request, obj, form, change) + if obj.id: + obj.purge_translation_cache() + return super().save_model(request, obj, form, change) + # ------------------------------------------------------------------------ diff --git a/feincms/module/medialibrary/models.py b/feincms/module/medialibrary/models.py index 0ef9debd8..056eb2852 100644 --- a/feincms/module/medialibrary/models.py +++ b/feincms/module/medialibrary/models.py @@ -1,8 +1,6 @@ # ------------------------------------------------------------------------ -# coding=utf-8 # ------------------------------------------------------------------------ -from __future__ import absolute_import, unicode_literals import os import re @@ -12,13 +10,15 @@ from django.dispatch.dispatcher import receiver from django.template.defaultfilters import slugify from django.utils import timezone -from django.utils.encoding import python_2_unicode_compatible -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import gettext_lazy as _ from feincms import settings from feincms.models import ExtensionsMixin from feincms.translations import ( - TranslatedObjectMixin, Translation, TranslatedObjectManager) + TranslatedObjectManager, + TranslatedObjectMixin, + Translation, +) from . import logger @@ -29,37 +29,42 @@ class CategoryManager(models.Manager): Simple manager which exists only to supply ``.select_related("parent")`` on querysets since we can't even __str__ efficiently without it. """ - def get_query_set(self): - return super(CategoryManager, self).get_query_set().select_related( - "parent") + + def get_queryset(self): + return super().get_queryset().select_related("parent") # ------------------------------------------------------------------------ -@python_2_unicode_compatible class Category(models.Model): """ These categories are meant primarily for organizing media files in the library. """ - title = models.CharField(_('title'), max_length=200) + title = models.CharField(_("title"), max_length=200) parent = models.ForeignKey( - 'self', blank=True, null=True, - related_name='children', limit_choices_to={'parent__isnull': True}, - verbose_name=_('parent')) + "self", + blank=True, + null=True, + on_delete=models.CASCADE, + related_name="children", + limit_choices_to={"parent__isnull": True}, + verbose_name=_("parent"), + ) - slug = models.SlugField(_('slug'), max_length=150) + slug = models.SlugField(_("slug"), max_length=150) class Meta: - ordering = ['parent__title', 'title'] - verbose_name = _('category') - verbose_name_plural = _('categories') + ordering = ["parent__title", "title"] + verbose_name = _("category") + verbose_name_plural = _("categories") + app_label = "medialibrary" objects = CategoryManager() def __str__(self): if self.parent_id: - return '%s - %s' % (self.parent.title, self.title) + return f"{self.parent.title} - {self.title}" return self.title @@ -67,7 +72,8 @@ def save(self, *args, **kwargs): if not self.slug: self.slug = slugify(self.title) - super(Category, self).save(*args, **kwargs) + super().save(*args, **kwargs) + save.alters_data = True def path_list(self): @@ -78,11 +84,10 @@ def path_list(self): return p def path(self): - return ' - '.join((f.title for f in self.path_list())) + return " - ".join(f.title for f in self.path_list()) # ------------------------------------------------------------------------ -@python_2_unicode_compatible class MediaFileBase(models.Model, ExtensionsMixin, TranslatedObjectMixin): """ Abstract media file class. Includes the @@ -91,26 +96,25 @@ class MediaFileBase(models.Model, ExtensionsMixin, TranslatedObjectMixin): """ file = models.FileField( - _('file'), max_length=255, - upload_to=settings.FEINCMS_MEDIALIBRARY_UPLOAD_TO) - type = models.CharField( - _('file type'), max_length=12, editable=False, - choices=()) - created = models.DateTimeField( - _('created'), editable=False, default=timezone.now) - copyright = models.CharField(_('copyright'), max_length=200, blank=True) + _("file"), max_length=255, upload_to=settings.FEINCMS_MEDIALIBRARY_UPLOAD_TO + ) + type = models.CharField(_("file type"), max_length=12, editable=False, choices=()) + created = models.DateTimeField(_("created"), editable=False, default=timezone.now) + copyright = models.CharField(_("copyright"), max_length=200, blank=True) file_size = models.IntegerField( - _("file size"), blank=True, null=True, editable=False) + _("file size"), blank=True, null=True, editable=False + ) categories = models.ManyToManyField( - Category, verbose_name=_('categories'), blank=True, null=True) + Category, verbose_name=_("categories"), blank=True + ) categories.category_filter = True class Meta: abstract = True - ordering = ['-created'] - verbose_name = _('media file') - verbose_name_plural = _('media files') + ordering = ["-created"] + verbose_name = _("media file") + verbose_name_plural = _("media files") objects = TranslatedObjectManager() @@ -119,7 +123,7 @@ class Meta: @classmethod def reconfigure(cls, upload_to=None, storage=None): - f = cls._meta.get_field('file') + f = cls._meta.get_field("file") # Ugh. Copied relevant parts from django/db/models/fields/files.py # FileField.__init__ (around line 225) if storage: @@ -134,27 +138,29 @@ def register_filetypes(cls, *types): cls.filetypes[0:0] = types choices = [t[0:2] for t in cls.filetypes] cls.filetypes_dict = dict(choices) - cls._meta.get_field('type').choices[:] = choices + cls._meta.get_field("type").choices = choices def __init__(self, *args, **kwargs): - super(MediaFileBase, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) if self.file: self._original_file_name = self.file.name def __str__(self): - trans = None + if settings.FEINCMS_MEDIAFILE_TRANSLATIONS: + trans = None + + try: + trans = self.translation + except models.ObjectDoesNotExist: + pass + except AttributeError: + pass + + if trans: + trans = "%s" % trans + if trans.strip(): + return trans - try: - trans = self.translation - except models.ObjectDoesNotExist: - pass - except AttributeError: - pass - - if trans: - trans = '%s' % trans - if trans.strip(): - return trans return os.path.basename(self.file.name) def get_absolute_url(self): @@ -162,16 +168,15 @@ def get_absolute_url(self): def determine_file_type(self, name): """ - >>> t = MediaFileBase() - >>> str(t.determine_file_type('foobar.jpg')) + >>> str(MediaFile().determine_file_type('foobar.jpg')) 'image' - >>> str(t.determine_file_type('foobar.PDF')) + >>> str(MediaFile().determine_file_type('foobar.PDF')) 'pdf' - >>> str(t.determine_file_type('foobar.jpg.pdf')) + >>> str(MediaFile().determine_file_type('foobar.jpg.pdf')) 'pdf' - >>> str(t.determine_file_type('foobar.jgp')) + >>> str(MediaFile().determine_file_type('foobar.jgp')) 'other' - >>> str(t.determine_file_type('foobar-jpg')) + >>> str(MediaFile().determine_file_type('foobar-jpg')) 'other' """ for type_key, type_name, type_test in self.filetypes: @@ -187,21 +192,24 @@ def save(self, *args, **kwargs): if self.file: try: self.file_size = self.file.size - except (OSError, IOError, ValueError) as e: - logger.error("Unable to read file size for %s: %s" % (self, e)) + except (OSError, ValueError) as e: + logger.error(f"Unable to read file size for {self}: {e}") - super(MediaFileBase, self).save(*args, **kwargs) + super().save(*args, **kwargs) - logger.info("Saved mediafile %d (%s, type %s, %d bytes)" % ( - self.id, self.file.name, self.type, self.file_size or 0)) + logger.info( + "Saved mediafile %d (%s, type %s, %d bytes)" + % (self.id, self.file.name, self.type, self.file_size or 0) + ) # User uploaded a new file. Try to get rid of the old file in # storage, to avoid having orphaned files hanging around. - if getattr(self, '_original_file_name', None): + if getattr(self, "_original_file_name", None): if self.file.name != self._original_file_name: self.delete_mediafile(self._original_file_name) self.purge_translation_cache() + save.alters_data = True def delete_mediafile(self, name=None): @@ -210,60 +218,83 @@ def delete_mediafile(self, name=None): try: self.file.storage.delete(name) except Exception as e: - logger.warn("Cannot delete media file %s: %s" % (name, e)) + logger.warn(f"Cannot delete media file {name}: {e}") # ------------------------------------------------------------------------ MediaFileBase.register_filetypes( # Should we be using imghdr.what instead of extension guessing? - ('image', _('Image'), lambda f: re.compile( - r'\.(bmp|jpe?g|jp2|jxr|gif|png|tiff?)$', re.IGNORECASE).search(f)), - ('video', _('Video'), lambda f: re.compile( - r'\.(mov|m[14]v|mp4|avi|mpe?g|qt|ogv|wmv|flv)$', - re.IGNORECASE).search(f)), - ('audio', _('Audio'), lambda f: re.compile( - r'\.(au|mp3|m4a|wma|oga|ram|wav)$', re.IGNORECASE).search(f)), - ('pdf', _('PDF document'), lambda f: f.lower().endswith('.pdf')), - ('swf', _('Flash'), lambda f: f.lower().endswith('.swf')), - ('txt', _('Text'), lambda f: f.lower().endswith('.txt')), - ('rtf', _('Rich Text'), lambda f: f.lower().endswith('.rtf')), - ('zip', _('Zip archive'), lambda f: f.lower().endswith('.zip')), - ('doc', _('Microsoft Word'), lambda f: re.compile( - r'\.docx?$', re.IGNORECASE).search(f)), - ('xls', _('Microsoft Excel'), lambda f: re.compile( - r'\.xlsx?$', re.IGNORECASE).search(f)), - ('ppt', _('Microsoft PowerPoint'), lambda f: re.compile( - r'\.pptx?$', re.IGNORECASE).search(f)), - ('other', _('Binary'), lambda f: True), # Must be last + ( + "image", + _("Image"), + lambda f: re.compile( + r"\.(bmp|jpe?g|jp2|jxr|gif|png|tiff?|webp)$", re.IGNORECASE + ).search(f), + ), + ( + "video", + _("Video"), + lambda f: re.compile( + r"\.(mov|m[14]v|mp4|avi|mpe?g|qt|ogv|wmv|flv)$", re.IGNORECASE + ).search(f), + ), + ( + "audio", + _("Audio"), + lambda f: re.compile(r"\.(au|mp3|m4a|wma|oga|ram|wav)$", re.IGNORECASE).search( + f + ), + ), + ("pdf", _("PDF document"), lambda f: f.lower().endswith(".pdf")), + ("swf", _("Flash"), lambda f: f.lower().endswith(".swf")), + ("txt", _("Text"), lambda f: f.lower().endswith(".txt")), + ("rtf", _("Rich Text"), lambda f: f.lower().endswith(".rtf")), + ("zip", _("Zip archive"), lambda f: f.lower().endswith(".zip")), + ( + "doc", + _("Microsoft Word"), + lambda f: re.compile(r"\.docx?$", re.IGNORECASE).search(f), + ), + ( + "xls", + _("Microsoft Excel"), + lambda f: re.compile(r"\.xlsx?$", re.IGNORECASE).search(f), + ), + ( + "ppt", + _("Microsoft PowerPoint"), + lambda f: re.compile(r"\.pptx?$", re.IGNORECASE).search(f), + ), + ("other", _("Binary"), lambda f: True), # Must be last ) # ------------------------------------------------------------------------ class MediaFile(MediaFileBase): - pass + class Meta: + app_label = "medialibrary" @receiver(post_delete, sender=MediaFile) def _mediafile_post_delete(sender, instance, **kwargs): instance.delete_mediafile() - logger.info("Deleted mediafile %d (%s)" % ( - instance.id, instance.file.name)) + logger.info("Deleted mediafile %d (%s)" % (instance.id, instance.file.name)) # ------------------------------------------------------------------------ -@python_2_unicode_compatible class MediaFileTranslation(Translation(MediaFile)): """ Translated media file caption and description. """ - caption = models.CharField(_('caption'), max_length=200) - description = models.TextField(_('description'), blank=True) + caption = models.CharField(_("caption"), max_length=1000) + description = models.TextField(_("description"), blank=True) class Meta: - verbose_name = _('media file translation') - verbose_name_plural = _('media file translations') - unique_together = ('parent', 'language_code') + verbose_name = _("media file translation") + verbose_name_plural = _("media file translations") + unique_together = ("parent", "language_code") + app_label = "medialibrary" def __str__(self): return self.caption diff --git a/feincms/module/medialibrary/thumbnail.py b/feincms/module/medialibrary/thumbnail.py index a70c2dc27..f833ee913 100644 --- a/feincms/module/medialibrary/thumbnail.py +++ b/feincms/module/medialibrary/thumbnail.py @@ -1,12 +1,10 @@ -from __future__ import absolute_import, unicode_literals - from feincms import settings from feincms.templatetags import feincms_thumbnail from feincms.utils import get_object -def default_admin_thumbnail(mediafile, dimensions='100x100', **kwargs): - if mediafile.type != 'image': +def default_admin_thumbnail(mediafile, dimensions="100x100", **kwargs): + if mediafile.type != "image": return None return feincms_thumbnail.thumbnail(mediafile.file, dimensions) @@ -15,9 +13,8 @@ def default_admin_thumbnail(mediafile, dimensions='100x100', **kwargs): _cached_thumbnailer = None -def admin_thumbnail(mediafile, dimensions='100x100'): +def admin_thumbnail(mediafile, dimensions="100x100"): global _cached_thumbnailer if not _cached_thumbnailer: - _cached_thumbnailer = get_object( - settings.FEINCMS_MEDIALIBRARY_THUMBNAIL) + _cached_thumbnailer = get_object(settings.FEINCMS_MEDIALIBRARY_THUMBNAIL) return _cached_thumbnailer(mediafile, dimensions=dimensions) diff --git a/feincms/module/medialibrary/zip.py b/feincms/module/medialibrary/zip.py index d563658f3..182a43ac1 100644 --- a/feincms/module/medialibrary/zip.py +++ b/feincms/module/medialibrary/zip.py @@ -1,5 +1,4 @@ # ------------------------------------------------------------------------ -# coding=utf-8 # ------------------------------------------------------------------------ # # Created by Martin J. Laubach on 2011-12-07 @@ -7,12 +6,11 @@ # # ------------------------------------------------------------------------ -from __future__ import absolute_import, unicode_literals import json -import zipfile import os import time +import zipfile from django.conf import settings as django_settings from django.core.files.base import ContentFile @@ -23,7 +21,7 @@ # ------------------------------------------------------------------------ -export_magic = 'feincms-export-01' +export_magic = "feincms-export-01" # ------------------------------------------------------------------------ @@ -47,9 +45,9 @@ def import_zipfile(category_id, overwrite, data): info = {} try: info = json.loads(z.comment) - if info['export_magic'] == export_magic: + if info["export_magic"] == export_magic: is_export_file = True - except: + except Exception: pass # If meta information, do we need to create any categories? @@ -57,21 +55,21 @@ def import_zipfile(category_id, overwrite, data): category_id_map = {} if is_export_file: for cat in sorted( - info.get('categories', []), - key=lambda k: k.get('level', 999)): + info.get("categories", []), key=lambda k: k.get("level", 999) + ): new_cat, created = Category.objects.get_or_create( - slug=cat['slug'], - title=cat['title']) - category_id_map[cat['id']] = new_cat - if created and cat.get('parent', 0): - parent_cat = category_id_map.get(cat.get('parent', 0), None) + slug=cat["slug"], title=cat["title"] + ) + category_id_map[cat["id"]] = new_cat + if created and cat.get("parent", 0): + parent_cat = category_id_map.get(cat.get("parent", 0), None) if parent_cat: new_cat.parent = parent_cat new_cat.save() count = 0 for zi in z.infolist(): - if not zi.filename.endswith('/'): + if not zi.filename.endswith("/"): bname = os.path.basename(zi.filename) if bname and not bname.startswith(".") and "." in bname: fname, ext = os.path.splitext(bname) @@ -95,36 +93,34 @@ def import_zipfile(category_id, overwrite, data): mf = MediaFile() if overwrite: mf.file.field.upload_to = wanted_dir - mf.copyright = info.get('copyright', '') - mf.file.save( - target_fname, - ContentFile(z.read(zi.filename)), - save=False) + mf.copyright = info.get("copyright", "") + mf.file.save(target_fname, ContentFile(z.read(zi.filename)), save=False) mf.save() found_metadata = False if is_export_file: try: - for tr in info['translations']: + for tr in info["translations"]: found_metadata = True - mt, mt_created =\ - MediaFileTranslation.objects.get_or_create( - parent=mf, language_code=tr['lang']) - mt.caption = tr['caption'] - mt.description = tr.get('description', None) + mt, mt_created = MediaFileTranslation.objects.get_or_create( + parent=mf, language_code=tr["lang"] + ) + mt.caption = tr["caption"] + mt.description = tr.get("description", None) mt.save() # Add categories mf.categories = ( category_id_map[cat_id] - for cat_id in info.get('categories', [])) + for cat_id in info.get("categories", []) + ) except Exception: pass if not found_metadata: mt = MediaFileTranslation() mt.parent = mf - mt.caption = fname.replace('_', ' ') + mt.caption = fname.replace("_", " ") mt.save() if category: @@ -139,10 +135,14 @@ def import_zipfile(category_id, overwrite, data): def export_zipfile(site, queryset): now = timezone.now() zip_name = "export_%s_%04d%02d%02d.zip" % ( - slugify(site.domain), now.year, now.month, now.day) + slugify(site.domain), + now.year, + now.month, + now.day, + ) zip_data = open(os.path.join(django_settings.MEDIA_ROOT, zip_name), "w") - zip_file = zipfile.ZipFile(zip_data, 'w', allowZip64=True) + zip_file = zipfile.ZipFile(zip_data, "w", allowZip64=True) # Save the used categories in the zip file's global comment used_categories = set() @@ -151,30 +151,38 @@ def export_zipfile(site, queryset): used_categories.update(cat.path_list()) info = { - 'export_magic': export_magic, - 'categories': [{ - 'id': cat.id, - 'title': cat.title, - 'slug': cat.slug, - 'parent': cat.parent_id or 0, - 'level': len(cat.path_list()), - } for cat in used_categories], + "export_magic": export_magic, + "categories": [ + { + "id": cat.id, + "title": cat.title, + "slug": cat.slug, + "parent": cat.parent_id or 0, + "level": len(cat.path_list()), + } + for cat in used_categories + ], } zip_file.comment = json.dumps(info) for mf in queryset: ctime = time.localtime(os.stat(mf.file.path).st_ctime) - info = json.dumps({ - 'copyright': mf.copyright, - 'categories': [cat.id for cat in mf.categories.all()], - 'translations': [{ - 'lang': t.language_code, - 'caption': t.caption, - 'description': t.description, - } for t in mf.translations.all()], - }) - - with open(mf.file.path, "r") as file_data: + info = json.dumps( + { + "copyright": mf.copyright, + "categories": [cat.id for cat in mf.categories.all()], + "translations": [ + { + "lang": t.language_code, + "caption": t.caption, + "description": t.description, + } + for t in mf.translations.all() + ], + } + ) + + with open(mf.file.path) as file_data: zip_info = zipfile.ZipInfo( filename=mf.file.name, date_time=( @@ -183,10 +191,13 @@ def export_zipfile(site, queryset): ctime.tm_mday, ctime.tm_hour, ctime.tm_min, - ctime.tm_sec)) + ctime.tm_sec, + ), + ) zip_info.comment = info zip_file.writestr(zip_info, file_data.read()) return zip_name + # ------------------------------------------------------------------------ diff --git a/feincms/module/mixins.py b/feincms/module/mixins.py index 9eefea0cd..63f5f89c7 100644 --- a/feincms/module/mixins.py +++ b/feincms/module/mixins.py @@ -1,17 +1,16 @@ -from __future__ import absolute_import, unicode_literals +from collections import OrderedDict from django.http import Http404 from django.template import Template -from django.utils.datastructures import SortedDict from django.utils.decorators import method_decorator from django.views import generic from django.views.generic.base import TemplateResponseMixin from feincms import settings -from feincms.views.decorators import standalone +from feincms.content.application.models import standalone -class ContentModelMixin(object): +class ContentModelMixin: """ Mixin for ``feincms.models.Base`` subclasses which need need some degree of additional control over the request-response cycle. @@ -30,7 +29,7 @@ def register_request_processor(cls, fn, key=None): always receives two arguments, the current object and the request. """ if cls.request_processors is None: - cls.request_processors = SortedDict() + cls.request_processors = OrderedDict() cls.request_processors[fn if key is None else key] = fn @classmethod @@ -41,7 +40,7 @@ def register_response_processor(cls, fn, key=None): request and the response. """ if cls.response_processors is None: - cls.response_processors = SortedDict() + cls.response_processors = OrderedDict() cls.response_processors[fn if key is None else key] = fn # TODO Implement admin_urlname templatetag protocol @@ -56,7 +55,7 @@ def app_label(self): return self._meta.app_label @property - def module_name(self): + def model_name(self): "See app_label" return self.__class__.__name__.lower() @@ -78,7 +77,7 @@ class ContentObjectMixin(TemplateResponseMixin): context_object_name = None def handler(self, request, *args, **kwargs): - if not hasattr(self.request, '_feincms_extra_context'): + if not hasattr(self.request, "_feincms_extra_context"): self.request._feincms_extra_context = {} r = self.run_request_processors() @@ -116,13 +115,13 @@ def get_template_names(self): # Hopefully someone else has a usable get_template_names() # implementation... - return super(ContentObjectMixin, self).get_template_names() + return super().get_template_names() def get_context_data(self, **kwargs): context = self.request._feincms_extra_context - context[self.context_object_name or 'feincms_object'] = self.object + context[self.context_object_name or "feincms_object"] = self.object context.update(kwargs) - return super(ContentObjectMixin, self).get_context_data(**context) + return super().get_context_data(**context) @property def __name__(self): @@ -139,7 +138,7 @@ def run_request_processors(self): also return a ``HttpResponse`` for shortcutting the rendering and returning that response immediately to the client. """ - if not getattr(self.object, 'request_processors', None): + if not getattr(self.object, "request_processors", None): return for fn in reversed(list(self.object.request_processors.values())): @@ -153,7 +152,7 @@ def run_response_processors(self, response): processors are called to modify the response, eg. for setting cache or expiration headers, keeping statistics, etc. """ - if not getattr(self.object, 'response_processors', None): + if not getattr(self.object, "response_processors", None): return for fn in self.object.response_processors.values(): @@ -171,9 +170,9 @@ def process_content_types(self): # did any content type successfully end processing? successful = False - for content in self.object.content.all_of_type(tuple( - self.object._feincms_content_types_with_process)): - + for content in self.object.content.all_of_type( + tuple(self.object._feincms_content_types_with_process) + ): try: r = content.process(self.request, view=self) if r in (True, False): @@ -190,15 +189,17 @@ def process_content_types(self): extra_context = self.request._feincms_extra_context - if (not settings.FEINCMS_ALLOW_EXTRA_PATH - and extra_context.get('extra_path', '/') != '/' - # XXX Already inside application content. I'm not sure - # whether this fix is really correct... - and not extra_context.get('app_config')): - raise Http404(str('Not found (extra_path %r on %r)') % ( - extra_context.get('extra_path', '/'), - self.object, - )) + if ( + not settings.FEINCMS_ALLOW_EXTRA_PATH + and extra_context.get("extra_path", "/") != "/" + # XXX Already inside application content. I'm not sure + # whether this fix is really correct... + and not extra_context.get("app_config") + ): + raise Http404( + "Not found (extra_path %r on %r)" + % (extra_context.get("extra_path", "/"), self.object) + ) def finalize_content_types(self, response): """ @@ -206,9 +207,9 @@ def finalize_content_types(self, response): returns the final response. """ - for content in self.object.content.all_of_type(tuple( - self.object._feincms_content_types_with_finalize)): - + for content in self.object.content.all_of_type( + tuple(self.object._feincms_content_types_with_finalize) + ): r = content.finalize(self.request, response) if r: return r @@ -228,4 +229,4 @@ def dispatch(self, request, *args, **kwargs): class StandaloneView(generic.View): @method_decorator(standalone) def dispatch(self, request, *args, **kwargs): - return super(StandaloneView, self).dispatch(request, *args, **kwargs) + return super().dispatch(request, *args, **kwargs) diff --git a/feincms/module/page/admin.py b/feincms/module/page/admin.py index 4abea4668..a4404b3cb 100644 --- a/feincms/module/page/admin.py +++ b/feincms/module/page/admin.py @@ -1,23 +1,21 @@ # ------------------------------------------------------------------------ -# coding=utf-8 # ------------------------------------------------------------------------ -from __future__ import absolute_import, unicode_literals from django.contrib import admin -from django.core.exceptions import ImproperlyConfigured -from django.db.models import FieldDoesNotExist +from django.core.exceptions import FieldDoesNotExist, ImproperlyConfigured + +from feincms import settings -from feincms import ensure_completely_loaded, settings -from .models import Page from .modeladmins import PageAdmin +from .models import Page + # ------------------------------------------------------------------------ if settings.FEINCMS_USE_PAGE_ADMIN: - ensure_completely_loaded() try: - Page._meta.get_field('template_key') + Page._meta.get_field("template_key") except FieldDoesNotExist: raise ImproperlyConfigured( "The page module requires a 'Page.register_templates()' call " diff --git a/feincms/module/page/extensions/excerpt.py b/feincms/module/page/extensions/excerpt.py index 25fe9004a..2212f2c50 100644 --- a/feincms/module/page/extensions/excerpt.py +++ b/feincms/module/page/extensions/excerpt.py @@ -2,10 +2,8 @@ Add an excerpt field to the page. """ -from __future__ import absolute_import, unicode_literals - from django.db import models -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import gettext_lazy as _ from feincms import extensions @@ -13,16 +11,17 @@ class Extension(extensions.Extension): def handle_model(self): self.model.add_to_class( - 'excerpt', + "excerpt", models.TextField( - _('excerpt'), + _("excerpt"), blank=True, help_text=_( - 'Add a brief excerpt summarizing the content' - ' of this page.'))) + "Add a brief excerpt summarizing the content of this page." + ), + ), + ) def handle_modeladmin(self, modeladmin): - modeladmin.add_extension_options(_('Excerpt'), { - 'fields': ('excerpt',), - 'classes': ('collapse',), - }) + modeladmin.add_extension_options( + _("Excerpt"), {"fields": ("excerpt",), "classes": ("collapse",)} + ) diff --git a/feincms/module/page/extensions/navigation.py b/feincms/module/page/extensions/navigation.py index 3edb296b2..915bd3863 100644 --- a/feincms/module/page/extensions/navigation.py +++ b/feincms/module/page/extensions/navigation.py @@ -7,15 +7,16 @@ be they real Page instances or extended navigation entries. """ -from __future__ import absolute_import, unicode_literals +import types +from collections import OrderedDict from django.db import models -from django.utils import six -from django.utils.translation import ugettext_lazy as _ +from django.utils.functional import cached_property +from django.utils.translation import gettext_lazy as _ from feincms import extensions -from feincms.utils import get_object from feincms._internal import monkeypatch_method +from feincms.utils import get_object, shorten_string class TypeRegistryMetaClass(type): @@ -26,13 +27,13 @@ class TypeRegistryMetaClass(type): """ def __init__(cls, name, bases, attrs): - if not hasattr(cls, 'types'): + if not hasattr(cls, "types"): cls.types = [] else: cls.types.append(cls) -class PagePretender(object): +class PagePretender: """ A PagePretender pretends to be a page, but in reality is just a shim layer that implements enough functionality to inject fake pages eg. into the @@ -42,11 +43,12 @@ class PagePretender(object): parameters on creation: title, url, level. If using the translation extension, also add language. """ + pk = None # emulate mptt properties to get the template tags working class _mptt_meta: - level_attr = 'level' + level_attr = "level" def __init__(self, **kwargs): for k, v in kwargs.items(): @@ -62,7 +64,7 @@ def get_level(self): return self.level def get_children(self): - """ overwrite this if you want nested extensions using recursetree """ + """overwrite this if you want nested extensions using recursetree""" return [] def available_translations(self): @@ -72,18 +74,17 @@ def get_original_translation(self, page): return page def short_title(self): - from feincms.utils import shorten_string return shorten_string(self.title) -class NavigationExtension(six.with_metaclass(TypeRegistryMetaClass)): +class NavigationExtension(metaclass=TypeRegistryMetaClass): """ Base class for all navigation extensions. The name attribute is shown to the website administrator. """ - name = _('navigation extension') + name = _("navigation extension") def children(self, page, **kwargs): """ @@ -100,38 +101,78 @@ def children(self, page, **kwargs): def navigation_extension_choices(): for ext in NavigationExtension.types: - if (issubclass(ext, NavigationExtension) - and ext is not NavigationExtension): - yield ('%s.%s' % (ext.__module__, ext.__name__), ext.name) + if issubclass(ext, NavigationExtension) and ext is not NavigationExtension: + yield (f"{ext.__module__}.{ext.__name__}", ext.name) + + +def get_extension_class(extension): + extension = get_object(extension) + if isinstance(extension, types.ModuleType): + return getattr(extension, "Extension") + return extension class Extension(extensions.Extension): - ident = 'navigation' # TODO actually use this + ident = "navigation" # TODO actually use this + navigation_extensions = None + + @cached_property + def _extensions(self): + if self.navigation_extensions is None: + return OrderedDict( + (f"{ext.__module__}.{ext.__name__}", ext) + for ext in NavigationExtension.types + if ( + issubclass(ext, NavigationExtension) + and ext is not NavigationExtension + ) + ) + + else: + return OrderedDict( + (f"{ext.__module__}.{ext.__name__}", ext) + for ext in map(get_extension_class, self.navigation_extensions) + ) def handle_model(self): + choices = [(path, ext.name) for path, ext in self._extensions.items()] + self.model.add_to_class( - 'navigation_extension', + "navigation_extension", models.CharField( - _('navigation extension'), - choices=navigation_extension_choices(), - blank=True, null=True, max_length=200, + _("navigation extension"), + choices=choices, + blank=True, + null=True, + max_length=200, help_text=_( - 'Select the module providing subpages for this page if' - ' you need to customize the navigation.'))) + "Select the module providing subpages for this page if" + " you need to customize the navigation." + ), + ), + ) + + extension = self @monkeypatch_method(self.model) def extended_navigation(self, **kwargs): if not self.navigation_extension: return self.children.in_navigation() - cls = get_object(self.navigation_extension, fail_silently=True) - if not cls or not callable(cls): - return self.children.in_navigation() + cls = None + + try: + cls = extension._extensions[self.navigation_extension] + except KeyError: + cls = get_object(self.navigation_extension, fail_silently=True) + extension._extensions[self.navigation_extension] = cls - return cls().children(self, **kwargs) + if cls: + return cls().children(self, **kwargs) + return self.children.in_navigation() def handle_modeladmin(self, modeladmin): - modeladmin.add_extension_options(_('Navigation extension'), { - 'fields': ('navigation_extension',), - 'classes': ('collapse',), - }) + modeladmin.add_extension_options( + _("Navigation extension"), + {"fields": ("navigation_extension",), "classes": ("collapse",)}, + ) diff --git a/feincms/module/page/extensions/navigationgroups.py b/feincms/module/page/extensions/navigationgroups.py new file mode 100644 index 000000000..67a0c4f53 --- /dev/null +++ b/feincms/module/page/extensions/navigationgroups.py @@ -0,0 +1,32 @@ +""" +Page navigation groups allow assigning pages to differing navigation lists +such as header, footer and what else. +""" + +from django.db import models +from django.utils.translation import gettext_lazy as _ + +from feincms import extensions + + +class Extension(extensions.Extension): + ident = "navigationgroups" + groups = [("default", _("Default")), ("footer", _("Footer"))] + + def handle_model(self): + self.model.add_to_class( + "navigation_group", + models.CharField( + _("navigation group"), + choices=self.groups, + default=self.groups[0][0], + max_length=20, + blank=True, + db_index=True, + ), + ) + + def handle_modeladmin(self, modeladmin): + modeladmin.add_extension_options("navigation_group") + modeladmin.extend_list("list_display", ["navigation_group"]) + modeladmin.extend_list("list_filter", ["navigation_group"]) diff --git a/feincms/module/page/extensions/relatedpages.py b/feincms/module/page/extensions/relatedpages.py index a8be7500f..e0b4a9340 100644 --- a/feincms/module/page/extensions/relatedpages.py +++ b/feincms/module/page/extensions/relatedpages.py @@ -2,28 +2,27 @@ Add a many-to-many relationship field to relate this page to other pages. """ -from __future__ import absolute_import, unicode_literals - from django.db import models -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import gettext_lazy as _ from feincms import extensions, settings class Extension(extensions.Extension): def handle_model(self): - self.model.add_to_class('related_pages', models.ManyToManyField( - settings.FEINCMS_DEFAULT_PAGE_MODEL, - blank=True, - null=True, - related_name='%(app_label)s_%(class)s_related', - help_text=_( - 'Select pages that should be listed as related content.'))) + self.model.add_to_class( + "related_pages", + models.ManyToManyField( + settings.FEINCMS_DEFAULT_PAGE_MODEL, + blank=True, + related_name="%(app_label)s_%(class)s_related", + help_text=_("Select pages that should be listed as related content."), + ), + ) def handle_modeladmin(self, modeladmin): - modeladmin.extend_list('filter_horizontal', ['related_pages']) + modeladmin.extend_list("filter_horizontal", ["related_pages"]) - modeladmin.add_extension_options(_('Related pages'), { - 'fields': ('related_pages',), - 'classes': ('collapse',), - }) + modeladmin.add_extension_options( + _("Related pages"), {"fields": ("related_pages",), "classes": ("collapse",)} + ) diff --git a/feincms/module/page/extensions/sites.py b/feincms/module/page/extensions/sites.py index 536df8710..4a4da11e8 100644 --- a/feincms/module/page/extensions/sites.py +++ b/feincms/module/page/extensions/sites.py @@ -1,9 +1,7 @@ -from __future__ import absolute_import, unicode_literals - from django.conf import settings from django.contrib.sites.models import Site from django.db import models -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import gettext_lazy as _ from feincms import extensions from feincms.module.page.models import PageManager @@ -16,13 +14,18 @@ def current_site(queryset): class Extension(extensions.Extension): def handle_model(self): self.model.add_to_class( - 'site', + "site", models.ForeignKey( - Site, verbose_name=_('Site'), default=settings.SITE_ID)) + Site, + verbose_name=_("Site"), + default=settings.SITE_ID, + on_delete=models.CASCADE, + ), + ) - PageManager.add_to_active_filters(current_site, key='current_site') + PageManager.add_to_active_filters(current_site, key="current_site") def handle_modeladmin(self, modeladmin): - modeladmin.extend_list('list_display', ['site']) - modeladmin.extend_list('list_filter', ['site']) - modeladmin.add_extension_options('site') + modeladmin.extend_list("list_display", ["site"]) + modeladmin.extend_list("list_filter", ["site"]) + modeladmin.add_extension_options("site") diff --git a/feincms/module/page/extensions/symlinks.py b/feincms/module/page/extensions/symlinks.py index 321f2cc48..52433da37 100644 --- a/feincms/module/page/extensions/symlinks.py +++ b/feincms/module/page/extensions/symlinks.py @@ -3,10 +3,8 @@ all content from the linked page. """ -from __future__ import absolute_import, unicode_literals - from django.db import models -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import gettext_lazy as _ from feincms import extensions from feincms._internal import monkeypatch_property @@ -14,25 +12,29 @@ class Extension(extensions.Extension): def handle_model(self): - self.model.add_to_class('symlinked_page', models.ForeignKey( - 'self', - blank=True, - null=True, - related_name='%(app_label)s_%(class)s_symlinks', - verbose_name=_('symlinked page'), - help_text=_('All content is inherited from this page if given.'))) + self.model.add_to_class( + "symlinked_page", + models.ForeignKey( + "self", + blank=True, + null=True, + on_delete=models.CASCADE, + related_name="%(app_label)s_%(class)s_symlinks", + verbose_name=_("symlinked page"), + help_text=_("All content is inherited from this page if given."), + ), + ) @monkeypatch_property(self.model) def content(self): - if not hasattr(self, '_content_proxy'): + if not hasattr(self, "_content_proxy"): if self.symlinked_page: - self._content_proxy = self.content_proxy_class( - self.symlinked_page) + self._content_proxy = self.content_proxy_class(self.symlinked_page) else: self._content_proxy = self.content_proxy_class(self) return self._content_proxy def handle_modeladmin(self, modeladmin): - modeladmin.extend_list('raw_id_fields', ['symlinked_page']) - modeladmin.add_extension_options('symlinked_page') + modeladmin.extend_list("raw_id_fields", ["symlinked_page"]) + modeladmin.add_extension_options("symlinked_page") diff --git a/feincms/module/page/extensions/titles.py b/feincms/module/page/extensions/titles.py index e16ec9bf9..7269c3564 100644 --- a/feincms/module/page/extensions/titles.py +++ b/feincms/module/page/extensions/titles.py @@ -4,10 +4,8 @@ you do that. """ -from __future__ import absolute_import, unicode_literals - from django.db import models -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import gettext_lazy as _ from feincms import extensions from feincms._internal import monkeypatch_property @@ -15,20 +13,30 @@ class Extension(extensions.Extension): def handle_model(self): - self.model.add_to_class('_content_title', models.TextField( - _('content title'), - blank=True, - help_text=_( - 'The first line is the main title, the following' - ' lines are subtitles.'))) - - self.model.add_to_class('_page_title', models.CharField( - _('page title'), - max_length=69, - blank=True, - help_text=_( - 'Page title for browser window. Same as title by' - 'default. Must not be longer than 70 characters.'))) + self.model.add_to_class( + "_content_title", + models.TextField( + _("content title"), + blank=True, + help_text=_( + "The first line is the main title, the following" + " lines are subtitles." + ), + ), + ) + + self.model.add_to_class( + "_page_title", + models.CharField( + _("page title"), + max_length=69, + blank=True, + help_text=_( + "Page title for browser window. Same as title by" + " default. Must be 69 characters or fewer." + ), + ), + ) @monkeypatch_property(self.model) def page_title(self): @@ -54,10 +62,10 @@ def content_title(self): @monkeypatch_property(self.model) def content_subtitle(self): - return '\n'.join(self._content_title.splitlines()[1:]) + return "\n".join(self._content_title.splitlines()[1:]) def handle_modeladmin(self, modeladmin): - modeladmin.add_extension_options(_('Titles'), { - 'fields': ('_content_title', '_page_title'), - 'classes': ('collapse',), - }) + modeladmin.add_extension_options( + _("Titles"), + {"fields": ("_content_title", "_page_title"), "classes": ("collapse",)}, + ) diff --git a/feincms/module/page/forms.py b/feincms/module/page/forms.py index f15d083e9..8e9415888 100644 --- a/feincms/module/page/forms.py +++ b/feincms/module/page/forms.py @@ -1,21 +1,14 @@ # ------------------------------------------------------------------------ -# coding=utf-8 # ------------------------------------------------------------------------ -from __future__ import absolute_import, unicode_literals import re +from django.apps import apps from django.contrib.admin.widgets import ForeignKeyRawIdWidget -from django.contrib.sites.models import Site -from django.db.models.loading import get_model from django.forms.models import model_to_dict -from django.forms.util import ErrorList from django.utils.safestring import mark_safe -from django.utils.translation import ugettext_lazy as _ - -from feincms import ensure_completely_loaded - +from django.utils.translation import gettext_lazy as _ from mptt.forms import MPTTAdminForm @@ -23,28 +16,38 @@ class RedirectToWidget(ForeignKeyRawIdWidget): def label_for_value(self, value): match = re.match( # XXX this regex would be available as .models.REDIRECT_TO_RE - r'^(?P\w+).(?P\w+):(?P\d+)$', - value) + r"^(?P\w+).(?P\w+):(?P\d+)$", + value, + ) if match: matches = match.groupdict() - model = get_model(matches['app_label'], matches['module_name']) + model = apps.get_model(matches["app_label"], matches["model_name"]) try: - instance = model._default_manager.get(pk=int(matches['pk'])) - return ' %s (%s)' % ( - instance, instance.get_absolute_url()) + instance = model._default_manager.get(pk=int(matches["pk"])) + return " {} ({})".format( + instance, + instance.get_absolute_url(), + ) except model.DoesNotExist: pass - return '' + return "" # ------------------------------------------------------------------------ class PageAdminForm(MPTTAdminForm): never_copy_fields = ( - 'title', 'slug', 'parent', 'active', 'override_url', - 'translation_of', '_content_title', '_page_title') + "title", + "slug", + "parent", + "active", + "override_url", + "translation_of", + "_content_title", + "_page_title", + ) @property def page_model(self): @@ -55,14 +58,11 @@ def page_manager(self): return self.page_model._default_manager def __init__(self, *args, **kwargs): - ensure_completely_loaded() - - if 'initial' in kwargs: - if 'parent' in kwargs['initial']: + if "initial" in kwargs: + if "parent" in kwargs["initial"]: # Prefill a few form values from the parent page try: - page = self.page_manager.get( - pk=kwargs['initial']['parent']) + page = self.page_manager.get(pk=kwargs["initial"]["parent"]) data = model_to_dict(page) @@ -75,78 +75,81 @@ def __init__(self, *args, **kwargs): if field in data: del data[field] - data.update(kwargs['initial']) + data.update(kwargs["initial"]) if page.template.child_template: - data['template_key'] = page.template.child_template - kwargs['initial'] = data + data["template_key"] = page.template.child_template + kwargs["initial"] = data except self.page_model.DoesNotExist: pass - elif 'translation_of' in kwargs['initial']: + elif "translation_of" in kwargs["initial"]: # Only if translation extension is active try: - page = self.page_manager.get( - pk=kwargs['initial']['translation_of']) + page = self.page_manager.get(pk=kwargs["initial"]["translation_of"]) original = page.original_translation data = { - 'translation_of': original.id, - 'template_key': original.template_key, - 'active': original.active, - 'in_navigation': original.in_navigation, + "translation_of": original.id, + "template_key": original.template_key, + "active": original.active, + "in_navigation": original.in_navigation, } if original.parent: try: - data['parent'] = original.parent.get_translation( - kwargs['initial']['language'] + data["parent"] = original.parent.get_translation( + kwargs["initial"]["language"] ).id except self.page_model.DoesNotExist: # ignore this -- the translation does not exist pass - data.update(kwargs['initial']) - kwargs['initial'] = data + data.update(kwargs["initial"]) + kwargs["initial"] = data except (AttributeError, self.page_model.DoesNotExist): pass # Not required, only a nice-to-have for the `redirect_to` field - modeladmin = kwargs.pop('modeladmin', None) - super(PageAdminForm, self).__init__(*args, **kwargs) - if modeladmin: + modeladmin = kwargs.pop("modeladmin", None) + super().__init__(*args, **kwargs) + if modeladmin and "redirect_to" in self.fields: # Note: Using `parent` is not strictly correct, but we can be # sure that `parent` always points to another page instance, # and that's good enough for us. - self.fields['redirect_to'].widget = RedirectToWidget( - self.page_model._meta.get_field('parent').rel, - modeladmin.admin_site) + field = self.page_model._meta.get_field("parent") + self.fields["redirect_to"].widget = RedirectToWidget( + field.remote_field if hasattr(field, "remote_field") else field.rel, # noqa + modeladmin.admin_site, + ) - if 'template_key' in self.fields: + if "template_key" in self.fields: choices = [] for key, template_name in self.page_model.TEMPLATE_CHOICES: template = self.page_model._feincms_templates[key] pages_for_template = self.page_model._default_manager.filter( - template_key=key) - pk = kwargs['instance'].pk if 'instance' in kwargs else None + template_key=key + ) + pk = kwargs["instance"].pk if kwargs.get("instance") else None other_pages_for_template = pages_for_template.exclude(pk=pk) if template.singleton and other_pages_for_template.exists(): continue # don't allow selection of singleton if in use if template.preview_image: - choices.append(( - template.key, - mark_safe('%s %s' % ( - template.preview_image, + choices.append( + ( template.key, - template.title, - )) - )) + mark_safe( + '%s %s' + % (template.preview_image, template.key, template.title) + ), + ) + ) else: choices.append((template.key, template.title)) - self.fields['template_key'].choices = choices + self.fields["template_key"].choices = choices def clean(self): - cleaned_data = super(PageAdminForm, self).clean() + cleaned_data = super().clean() # No need to think further, let the user correct errors first if self._errors: @@ -161,17 +164,21 @@ def clean(self): current_id = self.instance.id active_pages = active_pages.exclude(id=current_id) - if hasattr(Site, 'page_set') and 'site' in cleaned_data: - active_pages = active_pages.filter(site=cleaned_data['site']) + sites_is_installed = apps.is_installed("django.contrib.sites") + if sites_is_installed and "site" in cleaned_data: + active_pages = active_pages.filter(site=cleaned_data["site"]) # Convert PK in redirect_to field to something nicer for the future - redirect_to = cleaned_data.get('redirect_to') - if redirect_to and re.match(r'^\d+$', redirect_to): + redirect_to = cleaned_data.get("redirect_to") + if redirect_to and re.match(r"^\d+$", redirect_to): opts = self.page_model._meta - cleaned_data['redirect_to'] = '%s.%s:%s' % ( - opts.app_label, opts.module_name, redirect_to) + cleaned_data["redirect_to"] = "{}.{}:{}".format( + opts.app_label, + opts.model_name, + redirect_to, + ) - if not cleaned_data['active']: + if "active" in cleaned_data and not cleaned_data["active"]: # If the current item is inactive, we do not need to conduct # further validation. Note that we only check for the flag, not # for any other active filters. This is because we do not want @@ -179,12 +186,12 @@ def clean(self): # really won't be active at the same time. return cleaned_data - if cleaned_data['override_url']: - if active_pages.filter( - _cached_url=cleaned_data['override_url']).count(): - self._errors['override_url'] = ErrorList([ - _('This URL is already taken by an active page.')]) - del cleaned_data['override_url'] + if "override_url" in cleaned_data and cleaned_data["override_url"]: + if active_pages.filter(_cached_url=cleaned_data["override_url"]).count(): + self._errors["override_url"] = self.error_class( + [_("This URL is already taken by an active page.")] + ) + del cleaned_data["override_url"] return cleaned_data @@ -193,24 +200,27 @@ def clean(self): parent = self.page_manager.get(pk=current_id).parent else: # The user tries to create a new page - parent = cleaned_data['parent'] + parent = cleaned_data["parent"] if parent: - new_url = '%s%s/' % (parent._cached_url, cleaned_data['slug']) + new_url = "{}{}/".format(parent._cached_url, cleaned_data["slug"]) else: - new_url = '/%s/' % cleaned_data['slug'] + new_url = "/%s/" % cleaned_data["slug"] if active_pages.filter(_cached_url=new_url).count(): - self._errors['active'] = ErrorList([ - _('This URL is already taken by another active page.')]) - del cleaned_data['active'] + self._errors["active"] = self.error_class( + [_("This URL is already taken by another active page.")] + ) + del cleaned_data["active"] if parent and parent.template.enforce_leaf: - self._errors['parent'] = ErrorList( - [_('This page does not allow attachment of child pages')]) - del cleaned_data['parent'] + self._errors["parent"] = self.error_class( + [_("This page does not allow attachment of child pages")] + ) + del cleaned_data["parent"] return cleaned_data + # ------------------------------------------------------------------------ # ------------------------------------------------------------------------ diff --git a/feincms/module/page/modeladmins.py b/feincms/module/page/modeladmins.py index 3a6325fb7..2855a1c5f 100644 --- a/feincms/module/page/modeladmins.py +++ b/feincms/module/page/modeladmins.py @@ -1,21 +1,19 @@ # ------------------------------------------------------------------------ -# coding=utf-8 # ------------------------------------------------------------------------ -from __future__ import absolute_import, unicode_literals + +from functools import partial +from threading import local from django.conf import settings as django_settings -from django.core.exceptions import PermissionDenied +from django.contrib import admin, messages from django.contrib.contenttypes.models import ContentType -from django.contrib.staticfiles.templatetags.staticfiles import static -from django.contrib import admin -from django.contrib import messages -from django.core.urlresolvers import reverse +from django.core.exceptions import PermissionDenied from django.http import HttpResponseRedirect -from django.utils.functional import curry -from django.utils.translation import ugettext_lazy as _ +from django.templatetags.static import static +from django.urls import reverse +from django.utils.translation import gettext_lazy as _ -from feincms import ensure_completely_loaded from feincms import settings from feincms.admin import item_editor, tree_editor @@ -23,6 +21,9 @@ from .forms import PageAdminForm +_local = local() + + # ------------------------------------------------------------------------ class PageAdmin(item_editor.ItemEditor, tree_editor.TreeEditor): class Media: @@ -33,70 +34,70 @@ class Media: fieldset_insertion_index = 2 fieldsets = [ - (None, { - 'fields': [ - ('title', 'slug'), - ('active', 'in_navigation'), - ], - }), - (_('Other options'), { - 'classes': ['collapse'], - 'fields': [ - 'template_key', 'parent', 'override_url', 'redirect_to'], - }), + (None, {"fields": [("title", "slug"), ("active", "in_navigation")]}), + ( + _("Other options"), + { + "classes": ["collapse"], + "fields": ["template_key", "parent", "override_url", "redirect_to"], + }, + ), # <-- insertion point, extensions appear here, see insertion_index # above item_editor.FEINCMS_CONTENT_FIELDSET, ] readonly_fields = [] list_display = [ - 'short_title', 'is_visible_admin', 'in_navigation_toggle', 'template'] - list_filter = ['active', 'in_navigation', 'template_key', 'parent'] - search_fields = ['title', 'slug'] - prepopulated_fields = {'slug': ('title',)} + "short_title", + "is_visible_admin", + "in_navigation_toggle", + "template", + ] + list_filter = ["active", "in_navigation", "template_key", "parent"] + search_fields = ["title", "slug"] + prepopulated_fields = {"slug": ("title",)} - raw_id_fields = ['parent'] - radio_fields = {'template_key': admin.HORIZONTAL} + raw_id_fields = ["parent"] + radio_fields = {"template_key": admin.HORIZONTAL} @classmethod def add_extension_options(cls, *f): - if isinstance(f[-1], dict): # called with a fieldset + if isinstance(f[-1], dict): # called with a fieldset cls.fieldsets.insert(cls.fieldset_insertion_index, f) - f[1]['classes'] = list(f[1].get('classes', [])) - f[1]['classes'].append('collapse') - else: # assume called with "other" fields - cls.fieldsets[1][1]['fields'].extend(f) + f[1]["classes"] = list(f[1].get("classes", [])) + f[1]["classes"].append("collapse") + else: # assume called with "other" fields + cls.fieldsets[1][1]["fields"].extend(f) def __init__(self, model, admin_site): - ensure_completely_loaded() + if len(model._feincms_templates) > 4 and "template_key" in self.radio_fields: + del self.radio_fields["template_key"] - if len(model._feincms_templates) > 4 and \ - 'template_key' in self.radio_fields: - del(self.radio_fields['template_key']) - - super(PageAdmin, self).__init__(model, admin_site) + super().__init__(model, admin_site) in_navigation_toggle = tree_editor.ajax_editable_boolean( - 'in_navigation', _('in navigation')) + "in_navigation", _("in navigation") + ) def get_readonly_fields(self, request, obj=None): - readonly = super(PageAdmin, self).get_readonly_fields(request, obj=obj) + readonly = super().get_readonly_fields(request, obj=obj) if not settings.FEINCMS_SINGLETON_TEMPLATE_CHANGE_ALLOWED: if obj and obj.template and obj.template.singleton: - return tuple(readonly) + ('template_key',) + return tuple(readonly) + ("template_key",) return readonly def get_form(self, *args, **kwargs): - form = super(PageAdmin, self).get_form(*args, **kwargs) - return curry(form, modeladmin=self) + form = super().get_form(*args, **kwargs) + return partial(form, modeladmin=self) def _actions_column(self, page): - addable = getattr(page, 'feincms_addable', True) + addable = getattr(page, "feincms_addable", True) - preview_url = "../../r/%s/%s/" % ( + preview_url = "../../r/{}/{}/".format( ContentType.objects.get_for_model(self.model).id, - page.id) - actions = super(PageAdmin, self)._actions_column(page) + page.id, + ) + actions = super()._actions_column(page) if addable: if not page.template.enforce_leaf: @@ -104,61 +105,63 @@ def _actions_column(self, page): 0, '' '%s' - '' % ( + "" + % ( page.pk, - _('Add child page'), - static('feincms/img/icon_addlink.gif'), - _('Add child page'), - ) + _("Add child page"), + static("feincms/img/icon_addlink.gif"), + _("Add child page"), + ), ) actions.insert( 0, '' '%s' - '' % ( + "" + % ( preview_url, - _('View on site'), - static('feincms/img/selector-search.gif'), - _('View on site'), - ) + _("View on site"), + static("feincms/img/selector-search.gif"), + _("View on site"), + ), ) return actions def add_view(self, request, **kwargs): - kwargs['form_url'] = request.get_full_path() # Preserve GET parameters - if 'translation_of' in request.GET and 'language' in request.GET: + kwargs["form_url"] = request.get_full_path() # Preserve GET parameters + if "translation_of" in request.GET and "language" in request.GET: try: original = self.model._tree_manager.get( - pk=request.GET.get('translation_of')) + pk=request.GET.get("translation_of") + ) except (AttributeError, self.model.DoesNotExist): pass else: - language_code = request.GET['language'] - language = dict( - django_settings.LANGUAGES).get(language_code, '') - kwargs['extra_context'] = { - 'adding_translation': True, - 'title': _( - 'Add %(language)s translation of "%(page)s"') % { - 'language': language, - 'page': original, - }, - 'language_name': language, - 'translation_of': original, + language_code = request.GET["language"] + language = dict(django_settings.LANGUAGES).get(language_code, "") + kwargs["extra_context"] = { + "adding_translation": True, + "title": _('Add %(language)s translation of "%(page)s"') + % {"language": language, "page": original}, + "language_name": language, + "translation_of": original, } - return super(PageAdmin, self).add_view(request, **kwargs) + return super().add_view(request, **kwargs) def response_add(self, request, obj, *args, **kwargs): - response = super(PageAdmin, self).response_add( - request, obj, *args, **kwargs) - if ('parent' in request.GET - and '_addanother' in request.POST - and response.status_code in (301, 302)): + response = super().response_add(request, obj, *args, **kwargs) + if ( + "parent" in request.GET + and "_addanother" in request.POST + and response.status_code in (301, 302) + ): # Preserve GET parameters if we are about to add another page - response['Location'] += '?parent=%s' % request.GET['parent'] + response["Location"] += "?parent=%s" % request.GET["parent"] - if ('translation_of' in request.GET - and '_copy_content_from_original' in request.POST): + if ( + "translation_of" in request.GET + and "_copy_content_from_original" in request.POST + ): # Copy all contents for content_type in obj._feincms_content_types: if content_type.objects.filter(parent=obj).exists(): @@ -168,78 +171,87 @@ def response_add(self, request, obj, *args, **kwargs): try: original = self.model._tree_manager.get( - pk=request.GET.get('translation_of')) + pk=request.GET.get("translation_of") + ) original = original.original_translation obj.copy_content_from(original) obj.save() - self.message_user(request, _( - 'The content from the original translation has been copied' - ' to the newly created page.')) + self.message_user( + request, + _( + "The content from the original translation has been copied" + " to the newly created page." + ), + ) except (AttributeError, self.model.DoesNotExist): pass return response - def _refresh_changelist_caches(self, *args, **kwargs): - self._visible_pages = list( - self.model.objects.active().values_list('id', flat=True)) - def change_view(self, request, object_id, **kwargs): try: - return super(PageAdmin, self).change_view( - request, object_id, **kwargs) + return super().change_view(request, object_id, **kwargs) except PermissionDenied: messages.add_message( request, messages.ERROR, - _( - "You don't have the necessary permissions to edit this" - " object" - ) + _("You don't have the necessary permissions to edit this object"), ) - return HttpResponseRedirect(reverse('admin:page_page_changelist')) + return HttpResponseRedirect(reverse("admin:page_page_changelist")) def has_delete_permission(self, request, obj=None): if not settings.FEINCMS_SINGLETON_TEMPLATE_DELETION_ALLOWED: if obj and obj.template.singleton: return False - return super(PageAdmin, self).has_delete_permission(request, obj=obj) + return super().has_delete_permission(request, obj=obj) + + def changelist_view(self, request, *args, **kwargs): + _local.visible_pages = list( + self.model.objects.active().values_list("id", flat=True) + ) + return super().changelist_view(request, *args, **kwargs) + @admin.display(description=_("is active")) def is_visible_admin(self, page): """ Instead of just showing an on/off boolean, also indicate whether this page is not visible because of publishing dates or inherited status. """ - if not hasattr(self, "_visible_pages"): - # Sanity check in case this is not already defined - self._visible_pages = list() - - if page.parent_id and page.parent_id not in self._visible_pages: + if page.parent_id and page.parent_id not in _local.visible_pages: # parent page's invisibility is inherited - if page.id in self._visible_pages: - self._visible_pages.remove(page.id) + if page.id in _local.visible_pages: + _local.visible_pages.remove(page.id) return tree_editor.ajax_editable_boolean_cell( - page, 'active', override=False, text=_('inherited')) + page, "active", override=False, text=_("inherited") + ) - if page.active and page.id not in self._visible_pages: + if page.active and page.id not in _local.visible_pages: # is active but should not be shown, so visibility limited by # extension: show a "not active" return tree_editor.ajax_editable_boolean_cell( - page, 'active', override=False, text=_('extensions')) + page, "active", override=False, text=_("extensions") + ) + + return tree_editor.ajax_editable_boolean_cell(page, "active") - return tree_editor.ajax_editable_boolean_cell(page, 'active') - is_visible_admin.allow_tags = True - is_visible_admin.short_description = _('is active') - is_visible_admin.editable_boolean_field = 'active' + is_visible_admin.editable_boolean_field = "active" # active toggle needs more sophisticated result function def is_visible_recursive(self, page): + # Have to refresh visible_pages here, because TreeEditor.toggle_boolean + # will have changed the value when inside this code path. + _local.visible_pages = list( + self.model.objects.active().values_list("id", flat=True) + ) + retval = [] for c in page.get_descendants(include_self=True): retval.append(self.is_visible_admin(c)) return retval + is_visible_admin.editable_boolean_result = is_visible_recursive + # ------------------------------------------------------------------------ # ------------------------------------------------------------------------ diff --git a/feincms/module/page/models.py b/feincms/module/page/models.py index ac475ad9e..b269b9867 100644 --- a/feincms/module/page/models.py +++ b/feincms/module/page/models.py @@ -1,36 +1,22 @@ # ------------------------------------------------------------------------ -# coding=utf-8 # ------------------------------------------------------------------------ -from __future__ import absolute_import, unicode_literals -import re - -from django.core.cache import cache as django_cache from django.core.exceptions import PermissionDenied -from django.conf import settings as django_settings -from django.db import models -from django.db.models import Q, signals -from django.db.models.loading import get_model +from django.db import models, transaction +from django.db.models import Q from django.http import Http404 -from django.utils.encoding import python_2_unicode_compatible -from django.utils.translation import ugettext_lazy as _ - +from django.urls import reverse +from django.utils.translation import gettext_lazy as _ from mptt.models import MPTTModel, TreeManager from feincms import settings -from feincms.management.checker import check_database_schema from feincms.models import create_base_model from feincms.module.mixins import ContentModelMixin from feincms.module.page import processors +from feincms.utils import get_model_instance, match_model_string, shorten_string from feincms.utils.managers import ActiveAwareContentManagerMixin -from feincms.utils import path_to_cache_key, shorten_string - - -REDIRECT_TO_RE = re.compile( - r'^(?P\w+).(?P\w+):(?P\d+)$') - # ------------------------------------------------------------------------ class BasePageManager(ActiveAwareContentManagerMixin, TreeManager): @@ -40,8 +26,7 @@ class BasePageManager(ActiveAwareContentManagerMixin, TreeManager): """ # The fields which should be excluded when creating a copy. - exclude_from_copy = [ - 'id', 'tree_id', 'lft', 'rght', 'level', 'redirect_to'] + exclude_from_copy = ["id", "tree_id", "lft", "rght", "level", "redirect_to"] def page_for_path(self, path, raise404=False): """ @@ -52,14 +37,13 @@ def page_for_path(self, path, raise404=False): Page.objects.page_for_path(request.path) """ - stripped = path.strip('/') + stripped = path.strip("/") try: - page = self.active().get( - _cached_url='/%s/' % stripped if stripped else '/') + page = self.active().get(_cached_url="/%s/" % stripped if stripped else "/") if not page.are_ancestors_active(): - raise self.model.DoesNotExist('Parents are inactive.') + raise self.model.DoesNotExist("Parents are inactive.") return page @@ -80,33 +64,24 @@ def best_match_for_path(self, path, raise404=False): page with url '/photos/album/'. """ - paths = ['/'] - path = path.strip('/') - - # Cache path -> page resolving. - # We flush the cache entry on page saving, so the cache should always - # be up to date. - - ck = Page.path_to_cache_key(path) - page = django_cache.get(ck) - if page: - return page + paths = ["/"] + path = path.strip("/") if path: - tokens = path.split('/') - paths += [ - '/%s/' % '/'.join(tokens[:i]) - for i in range(1, len(tokens) + 1)] + tokens = path.split("/") + paths += ["/%s/" % "/".join(tokens[:i]) for i in range(1, len(tokens) + 1)] try: - page = self.active().filter(_cached_url__in=paths).extra( - select={'_url_length': 'LENGTH(_cached_url)'} - ).order_by('-_url_length')[0] + page = ( + self.active() + .filter(_cached_url__in=paths) + .extra(select={"_url_length": "LENGTH(_cached_url)"}) + .order_by("-_url_length")[0] + ) if not page.are_ancestors_active(): - raise IndexError('Parents are inactive.') + raise IndexError("Parents are inactive.") - django_cache.set(ck, page) return page except IndexError: @@ -129,8 +104,7 @@ def toplevel_navigation(self): return self.in_navigation().filter(parent__isnull=True) - def for_request(self, request, raise404=False, best_match=False, - path=None): + def for_request(self, request, raise404=False, best_match=False, path=None): """ Return a page for the request @@ -144,15 +118,15 @@ def for_request(self, request, raise404=False, best_match=False, could be determined. """ - if not hasattr(request, '_feincms_page'): + if not hasattr(request, "_feincms_page"): path = path or request.path_info or request.path if best_match: request._feincms_page = self.best_match_for_path( - path, raise404=raise404) + path, raise404=raise404 + ) else: - request._feincms_page = self.page_for_path( - path, raise404=raise404) + request._feincms_page = self.page_for_path(path, raise404=raise404) return request._feincms_page @@ -160,44 +134,64 @@ def for_request(self, request, raise404=False, best_match=False, # ------------------------------------------------------------------------ class PageManager(BasePageManager): pass -PageManager.add_to_active_filters(Q(active=True)) + + +PageManager.add_to_active_filters(Q(active=True), key="is_active") # ------------------------------------------------------------------------ -@python_2_unicode_compatible class BasePage(create_base_model(MPTTModel), ContentModelMixin): - active = models.BooleanField(_('active'), default=True) + active = models.BooleanField(_("active"), default=True) # structure and navigation - title = models.CharField(_('title'), max_length=200, help_text=_( - 'This title is also used for navigation menu items.')) + title = models.CharField( + _("title"), + max_length=200, + help_text=_("This title is also used for navigation menu items."), + ) slug = models.SlugField( - _('slug'), max_length=150, - help_text=_('This is used to build the URL for this page')) + _("slug"), + max_length=150, + help_text=_("This is used to build the URL for this page"), + ) parent = models.ForeignKey( - 'self', verbose_name=_('Parent'), blank=True, - null=True, related_name='children') + "self", + verbose_name=_("Parent"), + blank=True, + on_delete=models.CASCADE, + null=True, + related_name="children", + ) # Custom list_filter - see admin/filterspecs.py parent.parent_filter = True - in_navigation = models.BooleanField(_('in navigation'), default=False) + in_navigation = models.BooleanField(_("in navigation"), default=False) override_url = models.CharField( - _('override URL'), max_length=255, - blank=True, help_text=_( - 'Override the target URL. Be sure to include slashes at the ' - 'beginning and at the end if it is a local URL. This ' - 'affects both the navigation and subpages\' URLs.')) - redirect_to = models.CharField( - _('redirect to'), max_length=255, + _("override URL"), + max_length=255, blank=True, help_text=_( - 'Target URL for automatic redirects' - ' or the primary key of a page.')) + "Override the target URL. Be sure to include slashes at the " + "beginning and at the end if it is a local URL. This " + "affects both the navigation and subpages' URLs." + ), + ) + redirect_to = models.CharField( + _("redirect to"), + max_length=255, + blank=True, + help_text=_("Target URL for automatic redirects or the primary key of a page."), + ) _cached_url = models.CharField( - _('Cached URL'), max_length=255, blank=True, - editable=False, default='', db_index=True) + _("Cached URL"), + max_length=255, + blank=True, + editable=False, + default="", + db_index=True, + ) class Meta: - ordering = ['tree_id', 'lft'] + ordering = ["tree_id", "lft"] abstract = True objects = PageManager() @@ -213,12 +207,16 @@ def is_active(self): if not self.pk: return False + # No need to hit DB if page itself is inactive + if not self.active: + return False + pages = self.__class__.objects.active().filter( - tree_id=self.tree_id, - lft__lte=self.lft, - rght__gte=self.rght) + tree_id=self.tree_id, lft__lte=self.lft, rght__gte=self.rght + ) return pages.count() > self.level - is_active.short_description = _('is active') + + is_active.short_description = _("is active") def are_ancestors_active(self): """ @@ -236,11 +234,12 @@ def short_title(self): Title shortened for display. """ return shorten_string(self.title) - short_title.admin_order_field = 'title' - short_title.short_description = _('title') + + short_title.admin_order_field = "title" + short_title.short_description = _("title") def __init__(self, *args, **kwargs): - super(BasePage, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) # Cache a copy of the loaded _cached_url value so we can reliably # determine whether it has been changed in the save handler: self._original_cached_url = self._cached_url @@ -258,93 +257,69 @@ def save(self, *args, **kwargs): if self.override_url: self._cached_url = self.override_url elif self.is_root_node(): - self._cached_url = '/%s/' % self.slug + self._cached_url = "/%s/" % self.slug else: - self._cached_url = '%s%s/' % (self.parent._cached_url, self.slug) + self._cached_url = f"{self.parent._cached_url}{self.slug}/" cached_page_urls[self.id] = self._cached_url - super(BasePage, self).save(*args, **kwargs) - - # Okay, we have changed the page -- remove the old stale entry from the - # cache - self.invalidate_cache() - # If our cached URL changed we need to update all descendants to - # reflect the changes. Since this is a very expensive operation - # on large sites we'll check whether our _cached_url actually changed - # or if the updates weren't navigation related: - if self._cached_url == self._original_cached_url: - return + with transaction.atomic(): + super().save(*args, **kwargs) + + # If our cached URL changed we need to update all descendants to + # reflect the changes. Since this is a very expensive operation + # on large sites we'll check whether our _cached_url actually changed + # or if the updates weren't navigation related: + if self._cached_url != self._original_cached_url: + pages = self.get_descendants().order_by("lft") + + for page in pages: + if page.override_url: + page._cached_url = page.override_url + else: + # cannot be root node by definition + page._cached_url = "{}{}/".format( + cached_page_urls[page.parent_id], + page.slug, + ) + + cached_page_urls[page.id] = page._cached_url + super(BasePage, page).save() # do not recurse - pages = self.get_descendants().order_by('lft') - - for page in pages: - if page.override_url: - page._cached_url = page.override_url - else: - # cannot be root node by definition - page._cached_url = '%s%s/' % ( - cached_page_urls[page.parent_id], - page.slug) - - cached_page_urls[page.id] = page._cached_url - super(BasePage, page).save() # do not recurse save.alters_data = True def delete(self, *args, **kwargs): if not settings.FEINCMS_SINGLETON_TEMPLATE_DELETION_ALLOWED: if self.template.singleton: - raise PermissionDenied(_( - 'This %(page_class)s uses a singleton template, and ' - 'FEINCMS_SINGLETON_TEMPLATE_DELETION_ALLOWED=False' % { - 'page_class': self._meta.verbose_name})) - super(BasePage, self).delete(*args, **kwargs) - self.invalidate_cache() - delete.alters_data = True + raise PermissionDenied( + _( + "This %(page_class)s uses a singleton template, and " + "FEINCMS_SINGLETON_TEMPLATE_DELETION_ALLOWED=False" + % {"page_class": self._meta.verbose_name} + ) + ) + super().delete(*args, **kwargs) - # Remove the page from the url-to-page cache - def invalidate_cache(self): - ck = self.path_to_cache_key(self._original_cached_url) - django_cache.delete(ck) - ck = self.path_to_cache_key(self._cached_url) - django_cache.delete(ck) + delete.alters_data = True - @models.permalink def get_absolute_url(self): """ Return the absolute URL of this page. """ # result url never begins or ends with a slash - url = self._cached_url.strip('/') + url = self._cached_url.strip("/") if url: - return ('feincms_handler', (url,), {}) - return ('feincms_home', (), {}) + return reverse("feincms_handler", args=(url,)) + return reverse("feincms_home") def get_navigation_url(self): """ Return either ``redirect_to`` if it is set, or the URL of this page. """ - # :-( maybe this could be cleaned up a bit? - if not self.redirect_to or REDIRECT_TO_RE.match(self.redirect_to): - return self._cached_url - return self.redirect_to - - cache_key_components = [ - lambda p: getattr(django_settings, 'SITE_ID', 0), - lambda p: p._django_content_type.id, - lambda p: p.id, - ] - - def cache_key(self): - """ - Return a string that may be used as cache key for the current page. - The cache_key is unique for each content type and content instance. - - This function is here purely for your convenience. FeinCMS itself - does not use it in any way. - """ - return '-'.join(str(fn(self)) for fn in self.cache_key_components) + if self.redirect_to: + return self.get_redirect_to_target() + return self._cached_url def etag(self, request): """ @@ -365,75 +340,56 @@ def last_modified(self, request): """ return None - def get_redirect_to_target(self, request): + def get_redirect_to_page(self): """ This might be overriden/extended by extension modules. """ if not self.redirect_to: - return '' + return None # It might be an identifier for a different object - match = REDIRECT_TO_RE.match(self.redirect_to) + whereto = match_model_string(self.redirect_to) + if not whereto: + return None - # It's not, oh well. - if not match: - return self.redirect_to + return get_model_instance(*whereto) - matches = match.groupdict() - model = get_model(matches['app_label'], matches['module_name']) + def get_redirect_to_target(self, request=None): + """ + This might be overriden/extended by extension modules. + """ - if not model: + target_page = self.get_redirect_to_page() + if target_page is None: return self.redirect_to - try: - instance = model._default_manager.get(pk=int(matches['pk'])) - return instance.get_absolute_url() - except models.ObjectDoesNotExist: - pass - - return self.redirect_to - - @classmethod - def path_to_cache_key(cls, path): - prefix = "%s-FOR-URL" % cls.__name__.upper() - return path_to_cache_key(path.strip('/'), prefix=prefix) + return target_page.get_absolute_url() @classmethod - def register_default_processors(cls, frontend_editing=False): + def register_default_processors(cls): """ Register our default request processors for the out-of-the-box Page experience. """ cls.register_request_processor( - processors.redirect_request_processor, key='redirect') + processors.redirect_request_processor, key="redirect" + ) cls.register_request_processor( - processors.extra_context_request_processor, key='extra_context') - - if frontend_editing: - cls.register_request_processor( - processors.frontendediting_request_processor, - key='frontend_editing') - cls.register_response_processor( - processors.frontendediting_response_processor, - key='frontend_editing') + processors.extra_context_request_processor, key="extra_context" + ) # ------------------------------------------------------------------------ class Page(BasePage): class Meta: - ordering = ['tree_id', 'lft'] - verbose_name = _('page') - verbose_name_plural = _('pages') + ordering = ["tree_id", "lft"] + verbose_name = _("page") + verbose_name_plural = _("pages") + app_label = "page" # not yet # permissions = (("edit_page", _("Can edit page metadata")),) -Page.register_default_processors( - frontend_editing=settings.FEINCMS_FRONTEND_EDITING) -if settings.FEINCMS_CHECK_DATABASE_SCHEMA: - signals.post_syncdb.connect( - check_database_schema(Page, __name__), - weak=False) +Page.register_default_processors() # ------------------------------------------------------------------------ -# ------------------------------------------------------------------------ diff --git a/feincms/module/page/processors.py b/feincms/module/page/processors.py index 18e3573e3..6b8016b77 100644 --- a/feincms/module/page/processors.py +++ b/feincms/module/page/processors.py @@ -1,11 +1,13 @@ -from __future__ import absolute_import, print_function, unicode_literals - +import logging import re import sys from django.conf import settings as django_settings from django.http import Http404, HttpResponseRedirect -from django.utils.cache import add_never_cache_headers +from django.views.decorators.http import condition + + +logger = logging.getLogger(__name__) def redirect_request_processor(page, request): @@ -15,8 +17,14 @@ def redirect_request_processor(page, request): """ target = page.get_redirect_to_target(request) if target: - if request._feincms_extra_context.get('extra_path', '/') == '/': + extra_path = request._feincms_extra_context.get("extra_path", "/") + if extra_path == "/": return HttpResponseRedirect(target) + logger.debug( + "Page redirect on '%s' not taken because extra path '%s' present", + page.get_absolute_url(), + extra_path, + ) raise Http404() @@ -24,57 +32,38 @@ def extra_context_request_processor(page, request): """ Fills ``request._feincms_extra_context`` with a few useful variables. """ - request._feincms_extra_context.update({ - # XXX This variable name isn't accurate anymore. - 'in_appcontent_subpage': False, - 'extra_path': '/', - }) + request._feincms_extra_context.update( + { + # XXX This variable name isn't accurate anymore. + "in_appcontent_subpage": False, + "extra_path": "/", + } + ) url = page.get_absolute_url() if request.path != url: - request._feincms_extra_context.update({ - 'in_appcontent_subpage': True, - 'extra_path': re.sub( - '^' + re.escape(url.rstrip('/')), - '', - request.path, - ), - }) + request._feincms_extra_context.update( + { + "in_appcontent_subpage": True, + "extra_path": re.sub( + "^" + re.escape(url.rstrip("/")), "", request.path + ), + } + ) -def frontendediting_request_processor(page, request): +class __DummyResponse(dict): """ - Sets the frontend editing state in the cookie depending on the - ``frontend_editing`` GET parameter and the user's permissions. + This is a dummy class with enough behaviour of HttpResponse so we + can use the condition decorator without too much pain. """ - if 'frontend_editing' not in request.GET: - return - - response = HttpResponseRedirect(request.path) - if request.user.has_module_perms('page'): - try: - enable_fe = int(request.GET['frontend_editing']) > 0 - except ValueError: - enable_fe = False - - if enable_fe: - response.set_cookie(str('frontend_editing'), enable_fe) - else: - response.delete_cookie(str('frontend_editing')) - - # Redirect to cleanup URLs - return response + @property + def headers(self): + return self -def frontendediting_response_processor(page, request, response): - # Add never cache headers in case frontend editing is active - if (hasattr(request, 'COOKIES') - and request.COOKIES.get('frontend_editing', False)): - - if hasattr(response, 'add_post_render_callback'): - response.add_post_render_callback(add_never_cache_headers) - else: - add_never_cache_headers(response) + def has_header(self, what): + return False def etag_request_processor(page, request): @@ -82,19 +71,8 @@ def etag_request_processor(page, request): Short-circuits the request-response cycle if the ETag matches. """ - # XXX is this a performance concern? Does it create a new class - # every time the processor is called or is this optimized to a static - # class?? - class DummyResponse(dict): - """ - This is a dummy class with enough behaviour of HttpResponse so we - can use the condition decorator without too much pain. - """ - def has_header(page, what): - return False - def dummy_response_handler(*args, **kwargs): - return DummyResponse() + return __DummyResponse() def etagger(request, page, *args, **kwargs): etag = page.etag(request) @@ -104,20 +82,17 @@ def lastmodifier(request, page, *args, **kwargs): lm = page.last_modified() return lm - # Unavailable in Django 1.0 -- the current implementation of ETag support - # requires Django 1.1 unfortunately. - from django.views.decorators.http import condition - # Now wrap the condition decorator around our dummy handler: # the net effect is that we will be getting a DummyResponse from # the handler if processing is to continue and a non-DummyResponse # (should be a "304 not modified") if the etag matches. rsp = condition(etag_func=etagger, last_modified_func=lastmodifier)( - dummy_response_handler)(request, page) + dummy_response_handler + )(request, page) # If dummy then don't do anything, if a real response, return and # thus shortcut the request processing. - if not isinstance(rsp, DummyResponse): + if not isinstance(rsp, __DummyResponse): return rsp @@ -129,7 +104,7 @@ def etag_response_processor(page, request, response): """ etag = page.etag(request) if etag is not None: - response['ETag'] = '"' + etag + '"' + response["ETag"] = '"' + etag + '"' def debug_sql_queries_response_processor(verbose=False, file=sys.stderr): @@ -151,13 +126,16 @@ def debug_sql_queries_response_processor(verbose=False, file=sys.stderr): def processor(page, request, response): from django.db import connection - print_sql = lambda x: x try: import sqlparse - print_sql = lambda x: sqlparse.format( - x, reindent=True, keyword_case='upper') - except: - pass + + def print_sql(x): + return sqlparse.format(x, reindent=True, keyword_case="upper") + + except Exception: + + def print_sql(x): + return x if verbose: print("-" * 60, file=file) @@ -166,9 +144,10 @@ def processor(page, request, response): for q in connection.queries: i += 1 if verbose: - print("%d : [%s]\n%s\n" % ( - i, q['time'], print_sql(q['sql'])), file=file) - time += float(q['time']) + print( + "%d : [%s]\n%s\n" % (i, q["time"], print_sql(q["sql"])), file=file + ) + time += float(q["time"]) print("-" * 60, file=file) print("Total: %d queries, %.3f ms" % (i, time), file=file) diff --git a/feincms/module/page/sitemap.py b/feincms/module/page/sitemap.py index 7a56314e8..babe80f4b 100644 --- a/feincms/module/page/sitemap.py +++ b/feincms/module/page/sitemap.py @@ -1,12 +1,10 @@ # ------------------------------------------------------------------------ -# coding=utf-8 # ------------------------------------------------------------------------ -from __future__ import absolute_import, unicode_literals -from django.db.models import Max -from django.db.models import get_model +from django.apps import apps from django.contrib.sitemaps import Sitemap +from django.db.models import Max from feincms import settings @@ -17,10 +15,19 @@ class PageSitemap(Sitemap): The PageSitemap can be used to automatically generate sitemap.xml files for submission to index engines. See http://www.sitemaps.org/ for details. """ - def __init__(self, navigation_only=False, max_depth=0, changefreq=None, - queryset=None, filter=None, extended_navigation=False, - page_model=settings.FEINCMS_DEFAULT_PAGE_MODEL, - *args, **kwargs): + + def __init__( + self, + navigation_only=False, + max_depth=0, + changefreq=None, + queryset=None, + filter=None, + extended_navigation=False, + page_model=settings.FEINCMS_DEFAULT_PAGE_MODEL, + *args, + **kwargs, + ): """ The PageSitemap accepts the following parameters for customisation of the resulting sitemap.xml output: @@ -29,7 +36,7 @@ def __init__(self, navigation_only=False, max_depth=0, changefreq=None, will appear in the site map. * max_depth -- if set to a non-negative integer, will limit the sitemap generated to this page hierarchy depth. - * changefreq -- should be a string or callable specifiying the page + * changefreq -- should be a string or callable specifying the page update frequency, according to the sitemap protocol. * queryset -- pass in a query set to restrict the Pages to include in the site map. @@ -39,7 +46,7 @@ def __init__(self, navigation_only=False, max_depth=0, changefreq=None, extensions. If using PagePretender, make sure to include title, url, level, in_navigation and optionally modification_date. """ - super(PageSitemap, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) self.depth_cutoff = max_depth self.navigation_only = navigation_only self.changefreq = changefreq @@ -48,7 +55,7 @@ def __init__(self, navigation_only=False, max_depth=0, changefreq=None, if queryset is not None: self.queryset = queryset else: - Page = get_model(*page_model.split('.')) + Page = apps.get_model(*page_model.split(".")) self.queryset = Page.objects.active() def items(self): @@ -60,7 +67,7 @@ def items(self): if callable(base_qs): base_qs = base_qs() - self.max_depth = base_qs.aggregate(Max('level'))['level__max'] or 0 + self.max_depth = base_qs.aggregate(Max("level"))["level__max"] or 0 if self.depth_cutoff > 0: self.max_depth = min(self.depth_cutoff, self.max_depth) @@ -78,15 +85,13 @@ def items(self): for idx, page in enumerate(pages): if self.depth_cutoff > 0 and page.level == self.max_depth: continue - if getattr(page, 'navigation_extension', None): + if getattr(page, "navigation_extension", None): cnt = 0 for p in page.extended_navigation(): depth_too_deep = ( - self.depth_cutoff > 0 - and p.level > self.depth_cutoff) - not_in_nav = ( - self.navigation_only - and not p.in_navigation) + self.depth_cutoff > 0 and p.level > self.depth_cutoff + ) + not_in_nav = self.navigation_only and not p.in_navigation if depth_too_deep or not_in_nav: continue cnt += 1 @@ -98,7 +103,7 @@ def items(self): return pages def lastmod(self, obj): - return getattr(obj, 'modification_date', None) + return getattr(obj, "modification_date", None) # the priority is computed of the depth in the tree of a page # may we should make an extension to give control to the user for priority @@ -108,7 +113,7 @@ def priority(self, obj): the site. Top level get highest priority, then each level is decreased by per_level. """ - if getattr(obj, 'override_url', '') == '/': + if getattr(obj, "override_url", "") == "/": prio = 1.0 else: prio = 1.0 - (obj.level + 1) * self.per_level @@ -120,4 +125,5 @@ def priority(self, obj): return "%0.2g" % min(1.0, prio) + # ------------------------------------------------------------------------ diff --git a/feincms/module/page/templatetags/__init__.py b/feincms/module/page/templatetags/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/feincms/shortcuts.py b/feincms/shortcuts.py index adb47aa4e..1d2afb882 100644 --- a/feincms/shortcuts.py +++ b/feincms/shortcuts.py @@ -1,7 +1,4 @@ -from __future__ import absolute_import, unicode_literals - -from django.shortcuts import render_to_response -from django.template import RequestContext +from django.shortcuts import render from feincms.module.page.models import Page @@ -12,9 +9,6 @@ def render_to_response_best_match(request, template_name, dictionary=None): """ dictionary = dictionary or {} - dictionary['feincms_page'] = Page.objects.best_match_for_request(request) + dictionary["feincms_page"] = Page.objects.best_match_for_request(request) - return render_to_response( - template_name, - dictionary, - context_instance=RequestContext(request)) + return render(request, template_name, dictionary) diff --git a/feincms/signals.py b/feincms/signals.py index 2d4fca135..f75fec0b6 100644 --- a/feincms/signals.py +++ b/feincms/signals.py @@ -1,5 +1,4 @@ # ------------------------------------------------------------------------ -# coding=utf-8 # ------------------------------------------------------------------------ # # Created by Martin J. Laubach on 2011-08-01 @@ -7,14 +6,14 @@ # # ------------------------------------------------------------------------ -from __future__ import absolute_import, unicode_literals from django.dispatch import Signal + # ------------------------------------------------------------------------ # This signal is sent when an item editor managed object is completely # saved, especially including all foreign or manytomany dependencies. -itemeditor_post_save_related = Signal(providing_args=["instance", "created"]) +itemeditor_post_save_related = Signal() # ------------------------------------------------------------------------ diff --git a/feincms/static/feincms/frontend_editing.css b/feincms/static/feincms/frontend_editing.css deleted file mode 100644 index 208a0e734..000000000 --- a/feincms/static/feincms/frontend_editing.css +++ /dev/null @@ -1,68 +0,0 @@ -.fe_box { - position: relative; - opacity: 0.6; - width: 100%!important; -} - -.fe_box .content { - overflow: hidden; -} - -#fe_controls, #fe_tools { - z-index: 10000; - background-color: white; - /* CSS3 standard: */ - opacity: 0.8; - /* IE proprietary: */ - filter: alpha(opacity=80); -} - -#fe_controls { - position: fixed; - top: 0; - right: 0; - margin: 0; - font-size: smaller; - padding: 3px 3px 3px 4px; - border-left: solid #7f7f7f 2px; - border-bottom: solid #7f7f7f 2px; - border-bottom-left-radius: 3px; - -moz-border-radius-bottomleft: 3px; - -webkit-border-bottom-left-radius: 3px; -} - -#fe_controls > a { - display: inline-block; - border-radius: 2px; - -moz-border-radius: 2px; - -webkit-border-radius: 2px; - border: 1px solid #7f7f7f; - white-space: nowrap; - text-decoration: none; - padding: 1px 3px 2px; - background-color: #e8e8e8; - color: #000; -} - -#fe_controls > a:hover { - background-color: #b4d5ff; -} - -#fe_tools { - position: absolute; - right: 0px; - top: 0px; - display: none; - border: 1px solid #000; - font-size: 70%; -} - -#fe_tools a { - background: #000; - color: #fff; - text-decoration: none; - margin: 0 10px 0 0; - padding: 4px 5px; - display: block; - float: left; -} diff --git a/feincms/static/feincms/frontend_editing.js b/feincms/static/feincms/frontend_editing.js deleted file mode 100644 index 08dd3d5c5..000000000 --- a/feincms/static/feincms/frontend_editing.js +++ /dev/null @@ -1,40 +0,0 @@ -(function($){ - $(function(){ - feincms.fe_init_animations(); - - $("#fe_tools > a").live("click", function() { - var fe_box = $(this).parents('div.fe_box'); - - if (this.id == 'fe_tools_edit') { - res = fe_box.attr('id').match(/([^\-]+)-([^\-]+)-([^\-]+)-(\d+)-(\d+)/); - var base = feincms.admin_index + res[1] +"/"+ res[2]; - window.open(base+"/"+res[4]+'|'+res[3]+'|'+res[5]+'/', - 'fe_editor', - 'height=500,width=800,resizable=yes,scrollbars=yes'); - } - - return false; - }); - }); - - feincms.fe_init_animations = function() { - var fe_tools = $('#fe_tools'); - $('.fe_box').hover( - function(){ - $(this).css('background', '#e8e8ff').animate({'opacity': 1}, 100).append(fe_tools); - fe_tools.show(); - }, - function(){ - $(this).animate({'opacity': 0.6}, 100).css('background', 'none'); - fe_tools.hide(); - } - ); - }; - - feincms.fe_update_content = function(identifier, content) { - var region = $('#' + identifier); - region.animate({'opacity': 0}).html(content); - region.animate({'opacity': 1.5}).animate({'opacity': 0.6}); - feincms.fe_init_animations(); - }; -})(feincms.jQuery); diff --git a/feincms/static/feincms/ie_compat.js b/feincms/static/feincms/ie_compat.js deleted file mode 100644 index f10fa4b93..000000000 --- a/feincms/static/feincms/ie_compat.js +++ /dev/null @@ -1,98 +0,0 @@ -/* Re-implement some methods IE sadly does not */ - -if(typeof(Array.prototype.indexOf) == 'undefined') { - // indexOf() function prototype for IE6/7/8 compatibility, taken from - // JavaScript Standard Library - http://www.devpro.it/JSL/ - Array.prototype.indexOf=function(elm,i){ - var j=this.length; - if(!i)i=0; - if(i>=0){while(i>> 0; - if (len === 0) { - return -1; - } - - n = len; - if (arguments.length > 1) { - n = Number(arguments[1]); - if (n != n) { - n = 0; - } - else if (n != 0 && n != (1 / 0) && n != -(1 / 0)) { - n = (n > 0 || -1) * Math.floor(Math.abs(n)); - } - } - - for (k = n >= 0 - ? Math.min(n, len - 1) - : len - Math.abs(n); k >= 0; k--) { - if (k in t && t[k] === searchElement) { - return k; - } - } - return -1; - }; -} diff --git a/feincms/static/feincms/img/drag_order_circle.png b/feincms/static/feincms/img/drag_order_circle.png deleted file mode 100644 index 2e95561f1..000000000 Binary files a/feincms/static/feincms/img/drag_order_circle.png and /dev/null differ diff --git a/feincms/static/feincms/img/help.gif b/feincms/static/feincms/img/help.gif deleted file mode 100644 index 3b5142531..000000000 Binary files a/feincms/static/feincms/img/help.gif and /dev/null differ diff --git a/feincms/static/feincms/img/important.gif b/feincms/static/feincms/img/important.gif deleted file mode 100644 index 41d49438f..000000000 Binary files a/feincms/static/feincms/img/important.gif and /dev/null differ diff --git a/feincms/static/feincms/img/info.gif b/feincms/static/feincms/img/info.gif deleted file mode 100644 index c81828d1c..000000000 Binary files a/feincms/static/feincms/img/info.gif and /dev/null differ diff --git a/feincms/static/feincms/img/item_editor_form_title.png b/feincms/static/feincms/img/item_editor_form_title.png deleted file mode 100644 index bea3c14ea..000000000 Binary files a/feincms/static/feincms/img/item_editor_form_title.png and /dev/null differ diff --git a/feincms/static/feincms/img/nav-bg.gif b/feincms/static/feincms/img/nav-bg.gif deleted file mode 100644 index f8402b809..000000000 Binary files a/feincms/static/feincms/img/nav-bg.gif and /dev/null differ diff --git a/feincms/static/feincms/img/title.gif b/feincms/static/feincms/img/title.gif deleted file mode 100644 index f92b59665..000000000 Binary files a/feincms/static/feincms/img/title.gif and /dev/null differ diff --git a/feincms/static/feincms/img/wrench.png b/feincms/static/feincms/img/wrench.png deleted file mode 100644 index 5c8213fef..000000000 Binary files a/feincms/static/feincms/img/wrench.png and /dev/null differ diff --git a/feincms/static/feincms/item_editor.css b/feincms/static/feincms/item_editor.css index 461f10b55..0d2e47680 100644 --- a/feincms/static/feincms/item_editor.css +++ b/feincms/static/feincms/item_editor.css @@ -1,284 +1,308 @@ .navi_tab { - float:left; - padding: 5px 14px 5px 12px; - cursor:pointer; - border: 1px solid #ccc; - border-bottom:none; - min-width:100px; - margin-top:3px; - /* height:13px; */ - font-weight: bold; - font-size: 12px; - background-image:none; - color: #333333; - background: #E1E1E1 url(img/nav-bg.gif) top repeat-x; + float: left; + padding: 8px 10px; + cursor: pointer; + margin-top: 3px; + font-weight: bold; + font-size: 11px; + color: #666; + background: #f6f6f6; + border: 1px solid #eee; + text-transform: uppercase; } .tab_active { - margin-top: 0; - padding-top: 6px; - padding-bottom: 7px; - background: #7BA0C6 url('img/default-bg.gif') top repeat-x; - color: white; + background: #79aec8; + color: white; + border-color: #79aec8; } -#main { - clear:both; - padding: 10px 10px 10px 10px; - border: 1px solid #ccc; - margin: 0 0 10px 0; +#feincmsmain { + clear: both; + padding: 10px 10px 10px 10px; + border: 1px solid #eee; + margin: 0 0 10px 0; } + .panel { - display:none; - position:relative; - padding-bottom:50px; + display: none; + position: relative; + padding-bottom: 39px; } -.order-machine { - margin-bottom:10px; +.order-item { + margin: 0 0 10px 0; + position: relative; } -.order-item { - margin: 0 0 10px 0; - position:relative; +.order-item h2 { + background-image: url("img/arrow-move.png"); + background-repeat: no-repeat; + background-position: 6px 9px; } .order-item .handle { - position: absolute; - top: 0; - left: 0; - display: block; - height: 24px; - width: 15px; - cursor: move; + display: inline-block; + height: 14px; + width: 15px; + cursor: move; } .order-item .collapse { - cursor: pointer; - color: #444; - font-weight: normal; + cursor: pointer; + /*color: #444;*/ + font-weight: normal; + + border-bottom: 1px solid rgba(255, 255, 255, 0.25); +} + +.order-item .collapse:hover { + opacity: 0.7; } .item-delete { - cursor:pointer; - float:right; - margin: 2px 0px 0px 0; + cursor: pointer; + float: right; + margin: 4px 3px 0px 0; } -.highlight, .helper { - height: 25px; - margin: 0 0 10px 0; - border: none; - opacity: 0.3; - background: url('img/item_editor_form_title.png') no-repeat left bottom; +.highlight, +.helper { + height: 34px; + margin: 0 0 10px 0; + border: none; + opacity: 0.3; + background: #79aec8; } -.helper{ - height: 25px !important; - opacity: 1; +.helper { + height: 25px !important; + opacity: 1; } .button { - margin:5px; padding:5px; - font-weight: bold; - background-image:url('img/nav-bg.gif'); - cursor:pointer; - border: 1px solid #678; + margin: 5px; + padding: 5px; + font-weight: bold; + cursor: pointer; + border: 1px solid #678; } select { - max-width: 580px; + max-width: 580px; } -#main_wrapper { - margin: 10px 0 30px 0; +#feincmsmain_wrapper { + margin: 10px 0 30px 0; } -.clearfix { *zoom:1; } -.clearfix:before, .clearfix:after { content: " "; display: table; } -.clearfix:after { clear: both; } - -textarea { - width: 580px; - margin-top:5px; - margin-bottom:5px; +.clearfix:before, +.clearfix:after { + content: " "; + display: table; } - -.inline-group .tabular textarea { - width: auto; +.clearfix:after { + clear: both; } -.item-minimize { - width: 15px; - height:15px; - float: left; - cursor: pointer; - margin-left:-17px; +textarea { + width: 580px; + margin-top: 5px; + margin-bottom: 5px; } -.item-minimize-disabled { - width: 15px; - height:15px; - float: left; - margin-left:-17px; +.inline-group .tabular textarea { + width: auto; } .item-controls { - background: url('img/wrench.png') no-repeat 12px center; - padding-left:27px; - height: 35px; + position: absolute; + top: 2px; + right: 32px; } .item-control-unit { - float:left; - padding: 3px 10px 0 7px; - border-left: 1px solid #eee; + float: left; } -.item-control-unit:first-child { - border-left: none; +.item-controls select { + margin-left: 7px; } .machine-control { - height:70px; - padding: 5px 10px 5px 10px; - border: 1px solid #ccc; - background-color: #edf3fe; - position:absolute; - left:-11px; - bottom:-30px; - width: 100%; + padding: 5px 10px 5px 10px; + border: 1px solid #eee; + background-color: #f8f8f8; + position: absolute; + left: -11px; + bottom: -30px; + width: 100%; + + height: 55px; +} + +.machine-control .button { + padding-top: 6px; + padding-bottom: 6px; } .control-unit { - float:left; - padding-left: 5px; - padding-right:20px; - border-left: 1px solid #eee; + float: left; + padding: 0 20px 0 5px; + border-left: 1px solid #eee; } .control-unit:first-child { - border-left: none; + border-left: none; } .control-unit span { - font-weight:bold; + font-weight: bold; } a.actionbutton { - display: block; - background-repeat: no-repeat; - width:50px; - height:50px; - float:left; - margin: 5px 20px 0 0; - text-indent:-7000px; + display: block; + background-repeat: no-repeat; + width: 50px; + height: 50px; + float: left; + margin: 5px 0 0 20px; + text-indent: -7000px; + transition: none; } -a.richtextcontent { background: url(img/contenttypes.png) no-repeat 0 0; } -a.richtextcontent:hover { background-position: 0 -70px; } +a.richtextcontent { + background: url(img/contenttypes.png) no-repeat 0 0; +} +a.richtextcontent:hover { + background-position: 0 -70px; +} -a.imagecontent { background: url(img/contenttypes.png) no-repeat -70px 0; } -a.imagecontent:hover { background-position: -70px -70px; } +a.imagecontent { + background: url(img/contenttypes.png) no-repeat -70px 0; +} +a.imagecontent:hover { + background-position: -70px -70px; +} -a.gallerycontent { background: url(img/contenttypes.png) no-repeat -140px 0; } -a.gallerycontent:hover { background-position: -140px -70px; } +a.gallerycontent { + background: url(img/contenttypes.png) no-repeat -140px 0; +} +a.gallerycontent:hover { + background-position: -140px -70px; +} -a.oembedcontent { background: url(img/contenttypes.png) no-repeat -280px 0; } -a.oembedcontent:hover { background-position: -280px -70px; } +a.oembedcontent { + background: url(img/contenttypes.png) no-repeat -280px 0; +} +a.oembedcontent:hover { + background-position: -280px -70px; +} -a.pdfcontent { background: url(img/contenttypes.png) no-repeat -210px 0; } -a.pdfcontent:hover { background-position: -210px -70px; } +a.pdfcontent { + background: url(img/contenttypes.png) no-repeat -210px 0; +} +a.pdfcontent:hover { + background-position: -210px -70px; +} -a.audiocontent { background: url(img/contenttypes.png) no-repeat -350px 0; } -a.audiocontent:hover { background-position: -350px -70px; } +a.audiocontent { + background: url(img/contenttypes.png) no-repeat -350px 0; +} +a.audiocontent:hover { + background-position: -350px -70px; +} -.control-unit select, -.control-unit input { - position: relative; - top: 15px; +.control-unit select { + float: left; + position: relative; + top: 13px; } .empty-machine-msg { - margin:10px 0px 5px 300px; - font-style: italic; - font-size:14px; - color:#999; + margin: 10px 0px 20px 20px; + font-size: 14px; } td span select { - width:600px; -} - - -/* override Django admin styles */ -.order-machine fieldset.module > h2{ - padding: 4px 5px 6px 17px; - background: url(img/item_editor_form_title.png) no-repeat left bottom; + width: 600px; } .change-template-button { - margin-left: 7em; - padding-left: 30px; + margin-left: 7em; + padding-left: 30px; } /* Allow nested lists in error items */ ul.errorlist ul { - margin-left: 1em; - padding-left: 0; - list-style-type: square; + margin-left: 1em; + padding-left: 0; + list-style-type: square; } ul.errorlist li li { - /* Avoid repeating the warning image every time*/ - background-image:none; - padding: 0; + /* Avoid repeating the warning image every time*/ + background-image: none; + padding: 0; +} + +div.order-machine div.inline-related > h3 { + display: none; } -div.order-machine div.inline-related > h3{ - display: none; +.hidden-form-row { + display: none; } -.hidden-form-row{ - display: none; +#extension_options_wrapper { + border-bottom: 1px solid #eee; } +#extension_options > .module.aligned { + border-top: 1px solid #eee; + margin-bottom: -1px; +} /* various overrides */ -#id_redirect_to { width: 20em; } /* raw_id_fields act-a-like for redirect_to */ +#id_redirect_to { + width: 20em; +} /* raw_id_fields act-a-like for redirect_to */ +/* overwrite flat theme default label width because of problems with the CKEditor */ +.aligned .text label { + width: auto; +} /* django suit hacks */ /*********************/ -#suit-center #main { - clear:none; +#suit-center #feincmsmain { + clear: none; } #suit-center .panel { - padding-top: 15px; + padding-top: 15px; } #suit-center .form-horizontal .inline-related fieldset { - margin-top: 10px; + margin-top: 10px; } #suit-center .panel h2 { - color: white; - font-size: 13px; - line-height: 12px; - margin-left: 0; - text-shadow: none; + color: white; + font-size: 13px; + line-height: 12px; + margin-left: 0; + text-shadow: none; } #suit-center .item-delete { - margin: 3px 5px 0 0; + margin: 3px 5px 0 0; } #suit-center .order-item .handle { - height: 36px; + height: 36px; } #suit-center .order-machine .order-item { - margin-top: 10px; + margin-top: 10px; } - diff --git a/feincms/static/feincms/item_editor.js b/feincms/static/feincms/item_editor.js index 33c89c7b0..e8dd30dd7 100644 --- a/feincms/static/feincms/item_editor.js +++ b/feincms/static/feincms/item_editor.js @@ -1,630 +1,673 @@ -(function($){ - // Patch up urlify maps to generate nicer slugs in german - if(typeof(Downcoder) != "undefined"){ - Downcoder.Initialize() ; - Downcoder.map["ö"] = Downcoder.map["Ö"] = "oe"; - Downcoder.map["ä"] = Downcoder.map["Ä"] = "ae"; - Downcoder.map["ü"] = Downcoder.map["Ü"] = "ue"; +/* global Downcoder, django, feincms, ItemEditor, interpolate */ +/* global IMG_DELETELINK_PATH, REGION_MAP, REGION_NAMES, CONTENT_NAMES, FEINCMS_ITEM_EDITOR_GETTEXT, CONTENT_TYPE_BUTTONS */ +/* global contentblock_init_handlers, contentblock_move_handlers */ +/* global id_to_windowname */ +/* global template_regions */ + +;(($) => { + // Patch up urlify maps to generate nicer slugs in german + if (typeof Downcoder !== "undefined") { + Downcoder.Initialize() + Downcoder.map.ö = Downcoder.map.Ö = "oe" + Downcoder.map.ä = Downcoder.map.Ä = "ae" + Downcoder.map.ü = Downcoder.map.Ü = "ue" + } + + function feincms_gettext(s) { + // Unfortunately, we cannot use Django's jsi18n view for this + // because it only sends translations from the package + // "django.conf" -- our own djangojs domain strings won't be + // picked up + + if (FEINCMS_ITEM_EDITOR_GETTEXT[s]) return FEINCMS_ITEM_EDITOR_GETTEXT[s] + return s + } + + function create_new_item_from_form(form, modname, modvar) { + const fieldset = $("
    ").addClass( + `module aligned order-item item-wrapper-${modvar}`, + ) + const original_id_id = `#id_${form.attr("id")}-id` + + const wrp = ["

    "] + // If original has delete checkbox or this is a freshly added CT? Add delete link! + if ($(".delete", form).length || !$(original_id_id, form).val()) { + wrp.push(``) } - - function feincms_gettext(s) { - // Unfortunately, we cannot use Django's jsi18n view for this - // because it only sends translations from the package - // "django.conf" -- our own djangojs domain strings won't be - // picked up - - if (FEINCMS_ITEM_EDITOR_GETTEXT[s]) - return FEINCMS_ITEM_EDITOR_GETTEXT[s]; - return s; - } - - function create_new_item_from_form(form, modname, modvar){ - - var fieldset = $("
    ").addClass("module aligned order-item item-wrapper-" + modvar); - var original_id_id = '#id_' + form.attr('id') + '-id'; - - var wrp = ['

    ']; - // If original has delete checkbox or this is a freshly added CT? Add delete link! - if($('.delete', form).length || !$(original_id_id, form).val()) { - wrp.push(''); + wrp.push( + ` ${modname}

    `, + ) + wrp.push('
    ') + fieldset.append(wrp.join("")) + + fieldset.children(".item-content").append(form) //relocates, not clone + + $("
    ").addClass("item-controls").appendTo(fieldset) + + return fieldset + } + + const SELECTS = {} + function save_content_type_selects() { + $("#feincmsmain>.panel").each(function () { + SELECTS[this.id.replace(/_body$/, "")] = $( + "select[name=order-machine-add-select]", + this, + ) + .clone() + .removeAttr("name") + }) + } + + function update_item_controls(item, target_region_id) { + const item_controls = item.find(".item-controls") + item_controls.empty() + + // Insert control unit + const insert_control = $("
    ").addClass("item-control-unit") + const select_content = SELECTS[REGION_MAP[target_region_id]].clone() + + select_content.change(() => { + const modvar = select_content.val() + const modname = select_content.find("option:selected").html() + const new_fieldset = create_new_fieldset_from_module(modvar, modname) + add_fieldset(target_region_id, new_fieldset, { + where: "insertBefore", + relative_to: item, + animate: true, + }) + update_item_controls(new_fieldset, target_region_id) + + select_content.val("") + }) + insert_control.append(select_content) + item_controls.append(insert_control) + + // Move control unit + if (REGION_MAP.length > 1) { + const wrp = [] + wrp.push( + '
    ").attr("type", "button").addClass("button").attr("value", feincms_gettext('After')).click(function(){ - var modvar = select_content.val(); - var modname = select_content.find("option:selected").html(); - var new_fieldset = create_new_fieldset_from_module(modvar, modname); - add_fieldset(target_region_id, new_fieldset, {where:'insertAfter', relative_to:item, animate:true}); - update_item_controls(new_fieldset, target_region_id); - }); - var insert_before = $("").attr("type", "button").addClass("button").attr("value", feincms_gettext('Before')).click(function(){ - var modvar = select_content.val(); - var modname = select_content.find("option:selected").html(); - var new_fieldset = create_new_fieldset_from_module(modvar, modname); - add_fieldset(target_region_id, new_fieldset, {where:'insertBefore', relative_to:item, animate:true}); - update_item_controls(new_fieldset, target_region_id); - }); - insert_control.append("" + feincms_gettext('Insert new:') + "").append(" ").append(select_content).append(" ").append(insert_before).append(insert_after); - control_units.append(insert_control); - - // Move control unit - if (REGION_MAP.length > 1) { - var wrp = []; - wrp.push('
    '+feincms_gettext('Move to')+':
    '); - - var move_control = $(wrp.join("")); - move_control.find(".button").click(function(){ - var move_to = $(this).prev().val(); - move_item(REGION_MAP.indexOf(move_to), item); - }); - control_units.append(move_control); // Add new one - } - - // Controls animations - item_controls.find("*").hide(); - var is_hidden = true; - var mouseenter_timeout; - var mouseleave_timeout; - function hide_controls() { - item_controls.find("*").fadeOut(400); - is_hidden = true; - } - function show_controls() { - item_controls.find("*").fadeIn(200); - is_hidden = false; - } - item_controls.unbind('mouseleave'); // Unbind in case it's already been bound. - item_controls.mouseleave(function() { - clearTimeout(mouseenter_timeout); - mouseleave_timeout = setTimeout(hide_controls, 200); - }); - item_controls.unbind('mouseenter'); // Unbind in case it's already been bound. - item_controls.mouseenter(function() { - clearTimeout(mouseleave_timeout); - if (is_hidden) mouseenter_timeout = setTimeout(show_controls, 200); // To prevent the control bar to appear when mouse accidentally enters the zone. - }); + } + wrp.push("") + + const move_control = $(wrp.join("")) + move_control.find("select").change(function () { + const move_to = $(this).val() + move_item(REGION_MAP.indexOf(move_to), item) + }) + item_controls.append(move_control) // Add new one } + } + function create_new_fieldset_from_module(modvar, modname) { + const new_form = create_new_spare_form(modvar) + return create_new_item_from_form(new_form, modname, modvar) + } - function create_new_fieldset_from_module(modvar, modname) { - var new_form = create_new_spare_form(modvar); - return create_new_item_from_form(new_form, modname, modvar); - } - - function add_fieldset(region_id, item, how){ - /* `how` should be an object. + function add_fieldset(region_id, item, how) { + /* `how` should be an object. `how.where` should be one of: - 'append' -- last region - 'prepend' -- first region - 'insertBefore' -- insert before relative_to - 'insertAfter' -- insert after relative_to */ - // Default parameters - if (how) $.extend({ - where: 'append', - relative_to: undefined, - animate: false - }, how); - - item.hide(); - if(how.where == 'append' || how.where == 'prepend'){ - $("#"+ REGION_MAP[region_id] +"_body").children("div.order-machine")[how.where](item); - } - else if(how.where == 'insertBefore' || how.where == 'insertAfter'){ - if(how.relative_to){ - item[how.where](how.relative_to); - } - else{ - window.alert('DEBUG: invalid add_fieldset usage'); - return; - } - } - else{ - window.alert('DEBUG: invalid add_fieldset usage'); - return; - } - set_item_field_value(item, "region-choice-field", region_id); - init_contentblocks(); - - if (how.animate) { - item.fadeIn(800); - } - else { - item.show(); - } - } - - function create_new_spare_form(modvar) { - var old_form_count = parseInt($('#id_'+modvar+'_set-TOTAL_FORMS').val(), 10); - // **** UGLY CODE WARNING, avert your gaze! **** - // for some unknown reason, the add-button click handler function - // fails on the first triggerHandler call in some rare cases; - // we can detect this here and retry: - for(var i = 0; i < 2; i++){ - // Use Django's built-in inline spawing mechanism (Django 1.2+) - // must use django.jQuery since the bound function lives there: - django.jQuery('#'+modvar+'_set-group').find( - 'div.add-row > a').triggerHandler('click'); - var new_form_count = parseInt($('#id_'+modvar+'_set-TOTAL_FORMS').val(), 10); - if(new_form_count > old_form_count){ - return $('#'+modvar+'_set-'+(new_form_count-1)); - } - } - } - - function set_item_field_value(item, field, value) { - // item: DOM object for the item's fieldset. - // field: "order-field" | "delete-field" | "region-choice-field" - if (field=="delete-field") - item.find("."+field).attr("checked",value); - else if (field=="region-choice-field") { - var old_region_id = REGION_MAP.indexOf(item.find("."+field).val()); - item.find("."+field).val(REGION_MAP[value]); - - // show/hide the empty machine message in the source and - // target region. - old_region_item = $("#"+REGION_MAP[old_region_id]+"_body"); - if (old_region_item.children("div.order-machine").children().length == 0) - old_region_item.children("div.empty-machine-msg").show(); - else - old_region_item.children("div.empty-machine-msg").hide(); - - new_region_item = $("#"+REGION_MAP[value]+"_body"); - new_region_item.children("div.empty-machine-msg").hide(); - } - else - item.find("."+field).val(value); - } - - function move_item(region_id, item) { - poorify_rich(item); - item.fadeOut(800, function() { - add_fieldset(region_id, item, {where:'append'}); - richify_poor(item); - update_item_controls(item, region_id); - item.show(); - }); - } - - function poorify_rich(item){ - item.children(".item-content").hide(); - - for (var i=0; i v2 ? 1 : -1; + if (how.animate) { + item.fadeIn(800) + } else { + item.show() } - - function give_ordering_to_content_types() { - for (var i=0; i old_form_count) { + return $(`#${modvar}_set-${new_form_count - 1}`) } } - - function order_content_types_in_regions() { - for (var i=0; i { + add_fieldset(region_id, item, { where: "append" }) + richify_poor(item) + update_item_controls(item, region_id) + item.show() + }) + } + + function poorify_rich(item) { + item.children(".item-content").hide() + + for (let i = 0; i < contentblock_move_handlers.poorify.length; i++) + contentblock_move_handlers.poorify[i](item) + } + + function richify_poor(item) { + item.children(".item-content").show() + + for (let i = 0; i < contentblock_move_handlers.richify.length; i++) + contentblock_move_handlers.richify[i](item) + } + + function sort_by_ordering(e1, e2) { + const v1 = parseInt($(".order-field", e1).val(), 10) || 0 + const v2 = parseInt($(".order-field", e2).val(), 10) || 0 + return v1 > v2 ? 1 : -1 + } + + function give_ordering_to_content_types() { + for (let i = 0; i < REGION_MAP.length; i++) { + const container = $(`#${REGION_MAP[i]}_body div.order-machine`) + for (let j = 0; j < container.children().length; j++) { + set_item_field_value( + container.find(`fieldset.order-item:eq(${j})`), + "order-field", + j, + ) } } - - function init_contentblocks() { - for(var i=0; i